From 0d6c5d8d6e01eb2bdac5a054bbc0891bd4cb5f9b Mon Sep 17 00:00:00 2001 From: Sneha Edula Date: Mon, 4 May 2026 14:52:03 -0400 Subject: [PATCH 1/3] Update membership check by Task(IIR), get IIR details from Accounts --- .../api/iir_api/iir_api_federation_fetch.py | 19 +- .../api/iir_api/iir_api_get_issuers.py | 83 +- .../iir_api/iir_api_get_issuers_by_user.py | 73 +- .../iir_api_get_organization_iir_detail.py | 175 ++ .../api/iir_api/iir_api_is_user_a_member.py | 15 + .../iir_api_is_user_a_member_of_org.py | 15 + .../api/iir_api/iir_api_submit_to_iir.py | 138 +- .../api/iir_api/iir_api_update_issuer.py | 34 +- ce/iir/api/iir_client/models/__init__.py | 6 +- ...et_organization_iir_detail_response_200.py | 65 + .../{issuer_dto.py => pub_issuer_dto.py} | 18 +- ce/iir/csv_processor.py | 24 +- ce/iir/did_ops.py | 218 ++- ce/iir/openapi.json | 1727 +++++++++-------- ce/iir/swagger.json | 1168 ++++++----- generate.ps1 | 3 + 16 files changed, 2343 insertions(+), 1438 deletions(-) create mode 100644 ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py create mode 100644 ce/iir/api/iir_client/models/iir_api_get_organization_iir_detail_response_200.py rename ce/iir/api/iir_client/models/{issuer_dto.py => pub_issuer_dto.py} (90%) diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py b/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py index 3c0feb9..19091da 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py @@ -15,7 +15,6 @@ def _get_kwargs( *, - ctid: str, sub: str, ) -> dict[str, Any]: @@ -25,8 +24,6 @@ def _get_kwargs( params: dict[str, Any] = {} - params["ctid"] = ctid - params["sub"] = sub @@ -70,13 +67,11 @@ def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Res def sync_detailed( *, client: AuthenticatedClient | Client, - ctid: str, sub: str, ) -> Response[IIRApiFederationFetchResponse200]: """ Args: - ctid (str): sub (str): Raises: @@ -89,8 +84,7 @@ def sync_detailed( kwargs = _get_kwargs( - ctid=ctid, -sub=sub, + sub=sub, ) @@ -103,13 +97,11 @@ def sync_detailed( def sync( *, client: AuthenticatedClient | Client, - ctid: str, sub: str, ) -> IIRApiFederationFetchResponse200 | None: """ Args: - ctid (str): sub (str): Raises: @@ -123,7 +115,6 @@ def sync( return sync_detailed( client=client, -ctid=ctid, sub=sub, ).parsed @@ -131,13 +122,11 @@ def sync( async def asyncio_detailed( *, client: AuthenticatedClient | Client, - ctid: str, sub: str, ) -> Response[IIRApiFederationFetchResponse200]: """ Args: - ctid (str): sub (str): Raises: @@ -150,8 +139,7 @@ async def asyncio_detailed( kwargs = _get_kwargs( - ctid=ctid, -sub=sub, + sub=sub, ) @@ -164,13 +152,11 @@ async def asyncio_detailed( async def asyncio( *, client: AuthenticatedClient | Client, - ctid: str, sub: str, ) -> IIRApiFederationFetchResponse200 | None: """ Args: - ctid (str): sub (str): Raises: @@ -184,7 +170,6 @@ async def asyncio( return (await asyncio_detailed( client=client, -ctid=ctid, sub=sub, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py index 06eae6f..8b7940e 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py @@ -9,22 +9,41 @@ from ... import errors from ...models.iir_api_get_issuers_response_200 import IIRApiGetIssuersResponse200 +from ...types import UNSET, Unset from typing import cast def _get_kwargs( - + *, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, + ) -> dict[str, Any]: - + params: dict[str, Any] = {} + + params["page"] = page + + params["pageSize"] = page_size + + params["keyword"] = keyword + + params["task"] = task + + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + _kwargs: dict[str, Any] = { "method": "get", "url": "/api/iir/issuers", + "params": params, } @@ -58,9 +77,19 @@ def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Res def sync_detailed( *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> Response[IIRApiGetIssuersResponse200]: """ + Args: + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): + Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. @@ -71,7 +100,11 @@ def sync_detailed( kwargs = _get_kwargs( - + page=page, + page_size=page_size, + keyword=keyword, + task=task, + ) response = client.get_httpx_client().request( @@ -83,9 +116,19 @@ def sync_detailed( def sync( *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> IIRApiGetIssuersResponse200 | None: """ + Args: + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): + Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. @@ -97,15 +140,29 @@ def sync( return sync_detailed( client=client, +page=page, +page_size=page_size, +keyword=keyword, +task=task, ).parsed async def asyncio_detailed( *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> Response[IIRApiGetIssuersResponse200]: """ + Args: + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): + Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. @@ -116,7 +173,11 @@ async def asyncio_detailed( kwargs = _get_kwargs( - + page=page, + page_size=page_size, + keyword=keyword, + task=task, + ) response = await client.get_async_httpx_client().request( @@ -128,9 +189,19 @@ async def asyncio_detailed( async def asyncio( *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> IIRApiGetIssuersResponse200 | None: """ + Args: + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): + Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. @@ -142,5 +213,9 @@ async def asyncio( return (await asyncio_detailed( client=client, + page=page, + page_size=page_size, + keyword=keyword, + task=task, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py index c64ceeb..9a4f786 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py @@ -9,6 +9,7 @@ from ... import errors from ...models.iir_api_get_issuers_by_user_response_200 import IIRApiGetIssuersByUserResponse200 +from ...types import UNSET, Unset from typing import cast from uuid import UUID @@ -16,17 +17,35 @@ def _get_kwargs( user_id: UUID, + *, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> dict[str, Any]: - + params: dict[str, Any] = {} + + params["page"] = page + + params["pageSize"] = page_size + + params["keyword"] = keyword + + params["task"] = task + + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + _kwargs: dict[str, Any] = { "method": "get", "url": "/api/iir/users/{user_id}/issuers".format(user_id=quote(str(user_id), safe=""),), + "params": params, } @@ -61,11 +80,19 @@ def sync_detailed( user_id: UUID, *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> Response[IIRApiGetIssuersByUserResponse200]: """ Args: user_id (UUID): + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -78,6 +105,10 @@ def sync_detailed( kwargs = _get_kwargs( user_id=user_id, + page=page, + page_size=page_size, + keyword=keyword, + task=task, ) @@ -91,11 +122,19 @@ def sync( user_id: UUID, *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> IIRApiGetIssuersByUserResponse200 | None: """ Args: user_id (UUID): + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -108,7 +147,11 @@ def sync( return sync_detailed( user_id=user_id, -client=client, + client=client, + page=page, + page_size=page_size, + keyword=keyword, + task=task, ).parsed @@ -116,11 +159,19 @@ async def asyncio_detailed( user_id: UUID, *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> Response[IIRApiGetIssuersByUserResponse200]: """ Args: user_id (UUID): + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -133,6 +184,10 @@ async def asyncio_detailed( kwargs = _get_kwargs( user_id=user_id, +page=page, +page_size=page_size, +keyword=keyword, +task=task, ) @@ -146,11 +201,19 @@ async def asyncio( user_id: UUID, *, client: AuthenticatedClient | Client, + page: int | Unset = UNSET, + page_size: int | Unset = UNSET, + keyword: str | Unset = UNSET, + task: str | Unset = UNSET, ) -> IIRApiGetIssuersByUserResponse200 | None: """ Args: user_id (UUID): + page (int | Unset): + page_size (int | Unset): + keyword (str | Unset): + task (str | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -163,6 +226,10 @@ async def asyncio( return (await asyncio_detailed( user_id=user_id, -client=client, + client=client, + page=page, + page_size=page_size, + keyword=keyword, + task=task, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py new file mode 100644 index 0000000..c12561b --- /dev/null +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py @@ -0,0 +1,175 @@ +from http import HTTPStatus +from typing import Any, cast +from urllib.parse import quote + +import httpx + +from ...client import AuthenticatedClient, Client +from ...types import Response, UNSET +from ... import errors + +from ...models.iir_api_get_organization_iir_detail_response_200 import IIRApiGetOrganizationIIRDetailResponse200 +from typing import cast + + + +def _get_kwargs( + *, + ctid: str, + +) -> dict[str, Any]: + + + + + params: dict[str, Any] = {} + + params["ctid"] = ctid + + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/api/iir/getOrganizationIIRDetail", + "params": params, + } + + + return _kwargs + + + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> IIRApiGetOrganizationIIRDetailResponse200 | None: + if response.status_code == 200: + response_200 = IIRApiGetOrganizationIIRDetailResponse200.from_dict(response.json()) + + + + return response_200 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[IIRApiGetOrganizationIIRDetailResponse200]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: AuthenticatedClient | Client, + ctid: str, + +) -> Response[IIRApiGetOrganizationIIRDetailResponse200]: + """ + Args: + ctid (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[IIRApiGetOrganizationIIRDetailResponse200] + """ + + + kwargs = _get_kwargs( + ctid=ctid, + + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + +def sync( + *, + client: AuthenticatedClient | Client, + ctid: str, + +) -> IIRApiGetOrganizationIIRDetailResponse200 | None: + """ + Args: + ctid (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + IIRApiGetOrganizationIIRDetailResponse200 + """ + + + return sync_detailed( + client=client, +ctid=ctid, + + ).parsed + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, + ctid: str, + +) -> Response[IIRApiGetOrganizationIIRDetailResponse200]: + """ + Args: + ctid (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[IIRApiGetOrganizationIIRDetailResponse200] + """ + + + kwargs = _get_kwargs( + ctid=ctid, + + ) + + response = await client.get_async_httpx_client().request( + **kwargs + ) + + return _build_response(client=client, response=response) + +async def asyncio( + *, + client: AuthenticatedClient | Client, + ctid: str, + +) -> IIRApiGetOrganizationIIRDetailResponse200 | None: + """ + Args: + ctid (str): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + IIRApiGetOrganizationIIRDetailResponse200 + """ + + + return (await asyncio_detailed( + client=client, +ctid=ctid, + + )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py index 354fe29..aa909b9 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py @@ -17,6 +17,7 @@ def _get_kwargs( user_id: UUID, *, + task: str, ctid: str, ) -> dict[str, Any]: @@ -26,6 +27,8 @@ def _get_kwargs( params: dict[str, Any] = {} + params["task"] = task + params["ctid"] = ctid @@ -70,12 +73,14 @@ def sync_detailed( user_id: UUID, *, client: AuthenticatedClient | Client, + task: str, ctid: str, ) -> Response[IIRApiIsUserAMemberResponse200]: """ Args: user_id (UUID): + task (str): ctid (str): Raises: @@ -89,6 +94,7 @@ def sync_detailed( kwargs = _get_kwargs( user_id=user_id, +task=task, ctid=ctid, ) @@ -103,12 +109,14 @@ def sync( user_id: UUID, *, client: AuthenticatedClient | Client, + task: str, ctid: str, ) -> IIRApiIsUserAMemberResponse200 | None: """ Args: user_id (UUID): + task (str): ctid (str): Raises: @@ -123,6 +131,7 @@ def sync( return sync_detailed( user_id=user_id, client=client, +task=task, ctid=ctid, ).parsed @@ -131,12 +140,14 @@ async def asyncio_detailed( user_id: UUID, *, client: AuthenticatedClient | Client, + task: str, ctid: str, ) -> Response[IIRApiIsUserAMemberResponse200]: """ Args: user_id (UUID): + task (str): ctid (str): Raises: @@ -150,6 +161,7 @@ async def asyncio_detailed( kwargs = _get_kwargs( user_id=user_id, +task=task, ctid=ctid, ) @@ -164,12 +176,14 @@ async def asyncio( user_id: UUID, *, client: AuthenticatedClient | Client, + task: str, ctid: str, ) -> IIRApiIsUserAMemberResponse200 | None: """ Args: user_id (UUID): + task (str): ctid (str): Raises: @@ -184,6 +198,7 @@ async def asyncio( return (await asyncio_detailed( user_id=user_id, client=client, +task=task, ctid=ctid, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py index 61e8cd3..3d7c274 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py @@ -16,6 +16,7 @@ def _get_kwargs( *, ctid: str, + task: str, ) -> dict[str, Any]: @@ -26,6 +27,8 @@ def _get_kwargs( params["ctid"] = ctid + params["task"] = task + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} @@ -68,11 +71,13 @@ def sync_detailed( *, client: AuthenticatedClient | Client, ctid: str, + task: str, ) -> Response[IIRApiIsUserAMemberOfOrgResponse200]: """ Args: ctid (str): + task (str): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -85,6 +90,7 @@ def sync_detailed( kwargs = _get_kwargs( ctid=ctid, +task=task, ) @@ -98,11 +104,13 @@ def sync( *, client: AuthenticatedClient | Client, ctid: str, + task: str, ) -> IIRApiIsUserAMemberOfOrgResponse200 | None: """ Args: ctid (str): + task (str): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -116,6 +124,7 @@ def sync( return sync_detailed( client=client, ctid=ctid, +task=task, ).parsed @@ -123,11 +132,13 @@ async def asyncio_detailed( *, client: AuthenticatedClient | Client, ctid: str, + task: str, ) -> Response[IIRApiIsUserAMemberOfOrgResponse200]: """ Args: ctid (str): + task (str): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -140,6 +151,7 @@ async def asyncio_detailed( kwargs = _get_kwargs( ctid=ctid, +task=task, ) @@ -153,11 +165,13 @@ async def asyncio( *, client: AuthenticatedClient | Client, ctid: str, + task: str, ) -> IIRApiIsUserAMemberOfOrgResponse200 | None: """ Args: ctid (str): + task (str): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -171,5 +185,6 @@ async def asyncio( return (await asyncio_detailed( client=client, ctid=ctid, +task=task, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py b/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py index bd5e9cf..dc2e610 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py @@ -1,5 +1,6 @@ from http import HTTPStatus -from typing import Any +from typing import Any, cast +from urllib.parse import quote import httpx @@ -8,33 +9,49 @@ from ... import errors from ...models.iir_api_submit_to_iir_response_200 import IIRApiSubmitToIIRResponse200 -from ...models.issuer_dto import IssuerDTO +from ...models.pub_issuer_dto import PubIssuerDTO +from typing import cast + def _get_kwargs( *, - body: IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, + ) -> dict[str, Any]: headers: dict[str, Any] = {} + + + + + _kwargs: dict[str, Any] = { "method": "post", "url": "/api/iir/submitToIIR", } - if body is not UNSET: + if isinstance(body, PubIssuerDTO): _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + if isinstance(body, PubIssuerDTO): + _kwargs["data"] = body.to_dict() + + headers["Content-Type"] = "application/x-www-form-urlencoded" _kwargs["headers"] = headers return _kwargs -def _parse_response( - *, client: AuthenticatedClient | Client, response: httpx.Response -) -> IIRApiSubmitToIIRResponse200 | None: + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> IIRApiSubmitToIIRResponse200 | None: if response.status_code == 200: response_200 = IIRApiSubmitToIIRResponse200.from_dict(response.json()) + + + return response_200 if client.raise_on_unexpected_status: @@ -43,9 +60,7 @@ def _parse_response( return None -def _build_response( - *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Response[IIRApiSubmitToIIRResponse200]: +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[IIRApiSubmitToIIRResponse200]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -57,46 +72,113 @@ def _build_response( def sync_detailed( *, client: AuthenticatedClient | Client, - body: IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, + ) -> Response[IIRApiSubmitToIIRResponse200]: - kwargs = _get_kwargs(body=body) + """ + Args: + body (PubIssuerDTO): + body (PubIssuerDTO): - response = client.get_httpx_client().request(**kwargs) + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. - return _build_response(client=client, response=response) + Returns: + Response[IIRApiSubmitToIIRResponse200] + """ + + + kwargs = _get_kwargs( + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) def sync( *, client: AuthenticatedClient | Client, - body: IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, + ) -> IIRApiSubmitToIIRResponse200 | None: + """ + Args: + body (PubIssuerDTO): + body (PubIssuerDTO): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + IIRApiSubmitToIIRResponse200 + """ + + return sync_detailed( client=client, - body=body, - ).parsed +body=body, + ).parsed async def asyncio_detailed( *, client: AuthenticatedClient | Client, - body: IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, + ) -> Response[IIRApiSubmitToIIRResponse200]: - kwargs = _get_kwargs(body=body) + """ + Args: + body (PubIssuerDTO): + body (PubIssuerDTO): - response = await client.get_async_httpx_client().request(**kwargs) + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. - return _build_response(client=client, response=response) + Returns: + Response[IIRApiSubmitToIIRResponse200] + """ + + + kwargs = _get_kwargs( + body=body, + ) + + response = await client.get_async_httpx_client().request( + **kwargs + ) + + return _build_response(client=client, response=response) async def asyncio( *, client: AuthenticatedClient | Client, - body: IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, + ) -> IIRApiSubmitToIIRResponse200 | None: - return ( - await asyncio_detailed( - client=client, - body=body, - ) - ).parsed \ No newline at end of file + """ + Args: + body (PubIssuerDTO): + body (PubIssuerDTO): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + IIRApiSubmitToIIRResponse200 + """ + + + return (await asyncio_detailed( + client=client, +body=body, + + )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py b/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py index b3eca8d..e709c81 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py @@ -5,18 +5,18 @@ import httpx from ...client import AuthenticatedClient, Client -from ...types import Response, UNSET, Unset +from ...types import Response, UNSET from ... import errors from ...models.iir_api_update_issuer_response_200 import IIRApiUpdateIssuerResponse200 -from ...models.issuer_dto import IssuerDTO +from ...models.pub_issuer_dto import PubIssuerDTO from typing import cast def _get_kwargs( *, - body: IssuerDTO | IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -31,12 +31,12 @@ def _get_kwargs( "url": "/api/iir/updateIssuer", } - if isinstance(body, IssuerDTO): + if isinstance(body, PubIssuerDTO): _kwargs["json"] = body.to_dict() headers["Content-Type"] = "application/json" - if isinstance(body, IssuerDTO): + if isinstance(body, PubIssuerDTO): _kwargs["data"] = body.to_dict() headers["Content-Type"] = "application/x-www-form-urlencoded" @@ -72,13 +72,13 @@ def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Res def sync_detailed( *, client: AuthenticatedClient | Client, - body: IssuerDTO | IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, ) -> Response[IIRApiUpdateIssuerResponse200]: """ Args: - body (IssuerDTO): - body (IssuerDTO): + body (PubIssuerDTO): + body (PubIssuerDTO): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -103,13 +103,13 @@ def sync_detailed( def sync( *, client: AuthenticatedClient | Client, - body: IssuerDTO | IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, ) -> IIRApiUpdateIssuerResponse200 | None: """ Args: - body (IssuerDTO): - body (IssuerDTO): + body (PubIssuerDTO): + body (PubIssuerDTO): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -129,13 +129,13 @@ def sync( async def asyncio_detailed( *, client: AuthenticatedClient | Client, - body: IssuerDTO | IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, ) -> Response[IIRApiUpdateIssuerResponse200]: """ Args: - body (IssuerDTO): - body (IssuerDTO): + body (PubIssuerDTO): + body (PubIssuerDTO): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -160,13 +160,13 @@ async def asyncio_detailed( async def asyncio( *, client: AuthenticatedClient | Client, - body: IssuerDTO | IssuerDTO | Unset = UNSET, + body: PubIssuerDTO | PubIssuerDTO | Unset = UNSET, ) -> IIRApiUpdateIssuerResponse200 | None: """ Args: - body (IssuerDTO): - body (IssuerDTO): + body (PubIssuerDTO): + body (PubIssuerDTO): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. diff --git a/ce/iir/api/iir_client/models/__init__.py b/ce/iir/api/iir_client/models/__init__.py index dc18d1f..4c9f955 100644 --- a/ce/iir/api/iir_client/models/__init__.py +++ b/ce/iir/api/iir_client/models/__init__.py @@ -7,6 +7,7 @@ from .iir_api_get_issuer_by_did_response_200 import IIRApiGetIssuerByDidResponse200 from .iir_api_get_issuers_by_user_response_200 import IIRApiGetIssuersByUserResponse200 from .iir_api_get_issuers_response_200 import IIRApiGetIssuersResponse200 +from .iir_api_get_organization_iir_detail_response_200 import IIRApiGetOrganizationIIRDetailResponse200 from .iir_api_get_registry_resource_response_200 import IIRApiGetRegistryResourceResponse200 from .iir_api_is_user_a_member_of_org_response_200 import IIRApiIsUserAMemberOfOrgResponse200 from .iir_api_is_user_a_member_response_200 import IIRApiIsUserAMemberResponse200 @@ -16,7 +17,7 @@ from .iir_api_validate_jwt_signature_response_200 import IIRApiValidateJwtSignatureResponse200 from .iir_api_validate_key_response_200 import IIRApiValidateKeyResponse200 from .iir_api_validate_web_response_200 import IIRApiValidateWebResponse200 -from .issuer_dto import IssuerDTO +from .pub_issuer_dto import PubIssuerDTO from .safe_wait_handle import SafeWaitHandle from .save_challenge_token_request import SaveChallengeTokenRequest from .validate_jwt_request import ValidateJwtRequest @@ -31,6 +32,7 @@ "IIRApiGetIssuerByDidResponse200", "IIRApiGetIssuersByUserResponse200", "IIRApiGetIssuersResponse200", + "IIRApiGetOrganizationIIRDetailResponse200", "IIRApiGetRegistryResourceResponse200", "IIRApiIsUserAMemberOfOrgResponse200", "IIRApiIsUserAMemberResponse200", @@ -40,7 +42,7 @@ "IIRApiValidateJwtSignatureResponse200", "IIRApiValidateKeyResponse200", "IIRApiValidateWebResponse200", - "IssuerDTO", + "PubIssuerDTO", "SafeWaitHandle", "SaveChallengeTokenRequest", "ValidateJwtRequest", diff --git a/ce/iir/api/iir_client/models/iir_api_get_organization_iir_detail_response_200.py b/ce/iir/api/iir_client/models/iir_api_get_organization_iir_detail_response_200.py new file mode 100644 index 0000000..b3b988a --- /dev/null +++ b/ce/iir/api/iir_client/models/iir_api_get_organization_iir_detail_response_200.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + + + + + + + +T = TypeVar("T", bound="IIRApiGetOrganizationIIRDetailResponse200") + + + +@_attrs_define +class IIRApiGetOrganizationIIRDetailResponse200: + """ + """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + iir_api_get_organization_iir_detail_response_200 = cls( + ) + + + iir_api_get_organization_iir_detail_response_200.additional_properties = d + return iir_api_get_organization_iir_detail_response_200 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/ce/iir/api/iir_client/models/issuer_dto.py b/ce/iir/api/iir_client/models/pub_issuer_dto.py similarity index 90% rename from ce/iir/api/iir_client/models/issuer_dto.py rename to ce/iir/api/iir_client/models/pub_issuer_dto.py index 78b22fb..0be5f14 100644 --- a/ce/iir/api/iir_client/models/issuer_dto.py +++ b/ce/iir/api/iir_client/models/pub_issuer_dto.py @@ -18,12 +18,12 @@ -T = TypeVar("T", bound="IssuerDTO") +T = TypeVar("T", bound="PubIssuerDTO") @_attrs_define -class IssuerDTO: +class PubIssuerDTO: """ Attributes: ctid (str | Unset): @@ -72,12 +72,12 @@ def to_dict(self) -> dict[str, Any]: logo_uri = self.logo_uri valid_from: str | Unset = UNSET - if self.valid_from is not UNSET: - valid_from = self.valid_from.isoformat() if hasattr(self.valid_from, "isoformat") else self.valid_from + if not isinstance(self.valid_from, Unset): + valid_from = self.valid_from.isoformat() valid_until: str | Unset = UNSET - if self.valid_until is not UNSET: - valid_until = self.valid_until.isoformat() if hasattr(self.valid_until, "isoformat") else self.valid_until + if not isinstance(self.valid_until, Unset): + valid_until = self.valid_until.isoformat() field_dict: dict[str, Any] = {} @@ -148,7 +148,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - issuer_dto = cls( + pub_issuer_dto = cls( ctid=ctid, did=did, name=name, @@ -162,8 +162,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: ) - issuer_dto.additional_properties = d - return issuer_dto + pub_issuer_dto.additional_properties = d + return pub_issuer_dto @property def additional_keys(self) -> list[str]: diff --git a/ce/iir/csv_processor.py b/ce/iir/csv_processor.py index 88f4495..492be3b 100644 --- a/ce/iir/csv_processor.py +++ b/ce/iir/csv_processor.py @@ -22,7 +22,7 @@ from ce.iir.did_ops import ( call_is_user_member, - call_create_challenge, + call_create_challenges, call_get_registry_resource, call_save_challenge_token, call_validate_did_key, @@ -165,7 +165,7 @@ def fail(reason: str) -> None: if not ok: fail(err) else: - api_vm_ids = data.get("verificationMethodIds") or [] + api_vm_ids = data.get("VerificationMethodIds") or data.get("verificationMethodIds") or [] if not error: ok, err = validate_verification_method(vm, did, api_vm_ids) @@ -175,19 +175,25 @@ def fail(reason: str) -> None: challenge_uuid = "" challenge_payload = {} if not error: - ok, challenge, err = call_create_challenge( - ctid, vm, access_token, env.publisher_base, env.ssl_verify + ok, challenges_list, err = call_create_challenges( + ctid, [vm], access_token, env.publisher_base, env.ssl_verify ) if not ok: fail(err) + elif not challenges_list: + fail("createChallenges returned no challenges") else: - challenge_uuid = challenge.get("challenge") or challenge.get("Challenge", "") + challenge = next( + (c for c in challenges_list if (c.get("Did") or c.get("did")) == vm), + challenges_list[0], + ) + challenge_uuid = challenge.get("Challenge") or challenge.get("challenge", "") challenge_payload = { - "did": challenge.get("Did") or challenge.get("did", did), + "did": challenge.get("Did") or challenge.get("did", did), "challenge": challenge_uuid, - "aud": challenge.get("Aud") or challenge.get("aud", ""), - "iat": challenge.get("Iat") or challenge.get("iat", 0), - "exp": challenge.get("Exp") or challenge.get("exp", 0), + "aud": challenge.get("Aud") or challenge.get("aud", ""), + "iat": challenge.get("Iat") or challenge.get("iat", 0), + "exp": challenge.get("Exp") or challenge.get("exp", 0), "ctid": ctid, } diff --git a/ce/iir/did_ops.py b/ce/iir/did_ops.py index be1c8de..981f716 100644 --- a/ce/iir/did_ops.py +++ b/ce/iir/did_ops.py @@ -21,17 +21,18 @@ from ce.iir.api.iir_client.models.create_challenges_request import CreateChallengesRequest from ce.iir.api.iir_client.models.save_challenge_token_request import SaveChallengeTokenRequest from ce.iir.api.iir_client.models.validate_jwt_request import ValidateJwtRequest -from ce.iir.api.iir_client.models.issuer_dto import IssuerDTO +from ce.iir.api.iir_client.models.pub_issuer_dto import PubIssuerDTO from ce.iir.api.iir_client.api.iir_api.iir_api_is_user_a_member import sync_detailed as is_user_member_sync from ce.iir.api.iir_client.api.iir_api.iir_api_get_registry_resource import sync_detailed as get_registry_resource_sync +from ce.iir.api.iir_client.api.iir_api.iir_api_get_organization_iir_detail import sync_detailed as get_organization_iir_detail_sync from ce.iir.api.iir_client.api.iir_api.iir_api_validate_key import sync_detailed as validate_did_key_sync from ce.iir.api.iir_client.api.iir_api.iir_api_validate_web import sync_detailed as validate_did_web_sync from ce.iir.api.iir_client.api.iir_api.iir_api_create_challenges import sync_detailed as create_challenges_sync from ce.iir.api.iir_client.api.iir_api.iir_api_validate_jwt_signature import sync_detailed as validate_jwt_sync from ce.iir.api.iir_client.api.iir_api.iir_api_save_challenge_token import sync_detailed as save_challenge_token_sync from ce.iir.api.iir_client.api.iir_api.iir_api_submit_to_iir import sync_detailed as submit_to_iir_sync - +from ce.iir.api.iir_client.types import UNSET CE_GUID_RE = re.compile( r"^ce-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", @@ -47,6 +48,8 @@ "z6LS": "X25519", } +IIR_TASK = "IIR" + def _client(token: str, base_url: str, ssl_verify: bool) -> AuthenticatedClient: return AuthenticatedClient( base_url=base_url.rstrip("/"), @@ -55,6 +58,23 @@ def _client(token: str, base_url: str, ssl_verify: bool) -> AuthenticatedClient: timeout=60.0, ) + +def _parsed_to_dict(parsed: Any) -> dict: + + if not parsed: + return {} + if isinstance(parsed, dict): + return parsed + if hasattr(parsed, "to_dict"): + try: + d = parsed.to_dict() + if isinstance(d, dict): + return d + except Exception: + pass + return getattr(parsed, "additional_properties", {}) or {} + + def validate_ctid(ctid: str) -> tuple[bool, str]: if not ctid: return False, "CTID is empty" @@ -76,6 +96,31 @@ def validate_date(value: str, field_name: str) -> tuple[bool, str, str]: return False, f"{field_name} must be in MM/DD/YYYY format, got '{value}'", "" +def validate_date_range(valid_from: str, valid_until: str) -> tuple[bool, str]: + """Mirror the React page's both-or-neither + vu > vf cross-check. + + Inputs are the raw user values (any format accepted by validate_date). + """ + vf = (valid_from or "").strip() + vu = (valid_until or "").strip() + + if not vf and not vu: + return True, "" + if bool(vf) != bool(vu): + return False, "If you provide dates, both Valid From and Valid Until are required." + + ok_vf, err_vf, iso_vf = validate_date(vf, "Valid From") + if not ok_vf: + return False, err_vf + ok_vu, err_vu, iso_vu = validate_date(vu, "Valid Until") + if not ok_vu: + return False, err_vu + + if iso_vu <= iso_vf: + return False, "Valid Until must be after Valid From." + return True, "" + + def classify_did(did: str) -> tuple[str, str]: if not did: return "unknown", "DID is empty" @@ -116,20 +161,15 @@ def validate_verification_method(vm: str, did: str, api_vm_ids: list[str]) -> tu ) return True, "" -def call_is_user_member(user_id, ctid, token, publisher_base, ssl_verify) -> tuple[bool, str]: + +def call_is_user_member( + user_id, ctid, token, publisher_base, ssl_verify, task: str = IIR_TASK +) -> tuple[bool, str]: try: client = _client(token, publisher_base, ssl_verify) - response = is_user_member_sync(client=client, user_id=user_id, ctid=ctid) - - # Extract data - data = {} - parsed = response.parsed - if parsed: - if isinstance(parsed, dict): - data = parsed - else: - data = getattr(parsed, "additional_properties", {}) + response = is_user_member_sync(user_id, client=client, task=task, ctid=ctid) + data = _parsed_to_dict(response.parsed) valid = data.get("valid", False) if not valid: return False, "User is not a member of this organization" @@ -144,14 +184,7 @@ def call_get_registry_resource(ctid, token, publisher_base, ssl_verify) -> tuple client = _client(token, publisher_base, ssl_verify) response = get_registry_resource_sync(client=client, ctid=ctid) - data = {} - parsed = response.parsed - if parsed: - if isinstance(parsed, dict): - data = parsed - else: - data = getattr(parsed, "additional_properties", {}) - + data = _parsed_to_dict(response.parsed) exists = data.get("ExistsInRegistry", False) if not exists: return False, data, f"CTID '{ctid}' not found in registry" @@ -161,6 +194,56 @@ def call_get_registry_resource(ctid, token, publisher_base, ssl_verify) -> tuple except Exception as e: return False, {}, f"Registry API request failed: {e}" + +def call_get_organization_iir_detail( + ctid, token, publisher_base, ssl_verify +) -> tuple[bool, dict, str]: + try: + client = _client(token, publisher_base, ssl_verify) + response = get_organization_iir_detail_sync(client=client, ctid=ctid) + + envelope = _parsed_to_dict(response.parsed) + if not envelope: + return False, {}, "getOrganizationIIRDetail returned empty" + + if not envelope.get("valid", False): + return False, envelope, "Organization IIR detail not available" + + inner = envelope.get("data") or {} + if not isinstance(inner, dict): + inner = {} + + return True, inner, "" + + except Exception as e: + return False, {}, f"getOrganizationIIRDetail request failed: {e}" + + +def merge_autofill(registry: dict, accounts: dict) -> dict: + + name = registry.get("Name", "") or "" + registry_uri = registry.get("CredentialRegistryUri", "") or "" + subject_webpage = registry.get("SubjectWebpage", "") or "" + image = registry.get("Image", "") or "" + legal_name = registry.get("LegalName", "") or "" + logo_base64 = registry.get("LogoBase64", "") or "" + + if accounts: + if accounts.get("LegalName"): + legal_name = accounts["LegalName"] + if not image and accounts.get("LogoUrl"): + image = accounts["LogoUrl"] + + return { + "name": name, + "legal_name": legal_name, + "registry_uri": registry_uri, + "subject_webpage": subject_webpage, + "image": image, + "logo_base64": logo_base64, + } + + def call_validate_did_key( did: str, alg: str, token: str, publisher_base: str, ssl_verify: bool ) -> tuple[bool, dict, str]: @@ -178,7 +261,7 @@ def call_validate_did_key( if response.status_code >= 400: return False, {}, f"validateDidKey error {response.status_code}" - return True, (response.parsed.to_dict() if response.parsed is not None else {}), "" + return True, _parsed_to_dict(response.parsed), "" except Exception as e: return False, {}, f"validateDidKey request failed: {e}" @@ -202,25 +285,49 @@ def call_validate_did_web( if response.status_code >= 400: return False, {}, f"validateDidWeb error {response.status_code}" - data = response.parsed or {} - if hasattr(data, "to_dict"): - data = data.to_dict() - - return True, data if isinstance(data, dict) else {}, "" + return True, _parsed_to_dict(response.parsed), "" except Exception as e: return False, {}, f"validateDidWeb request failed: {e}" -def call_create_challenge( - ctid: str, vm_id: str, token: str, publisher_base: str, ssl_verify: bool -) -> tuple[bool, dict, str]: +def call_validate_did( + did: str, alg: str, token: str, publisher_base: str, ssl_verify: bool +) -> tuple[bool, list[str], dict, str]: + + kind, kind_err = classify_did(did) + if kind == "unknown": + return False, [], {}, kind_err or "Unsupported DID method" + + if kind == "did:key": + ok_pfx, err_pfx = validate_did_key_prefix(did, alg) + if not ok_pfx: + return False, [], {}, err_pfx + ok, data, err = call_validate_did_key(did, alg, token, publisher_base, ssl_verify) + else: + ok, data, err = call_validate_did_web(did, token, publisher_base, ssl_verify) + + if not ok: + return False, [], data, err + + vm_ids = data.get("VerificationMethodIds") or [] + if not isinstance(vm_ids, list): + vm_ids = [] + return True, vm_ids, data, "" + + +def call_create_challenges( + ctid: str, vm_ids: list[str], token: str, publisher_base: str, ssl_verify: bool +) -> tuple[bool, list[dict], str]: + if not vm_ids: + return False, [], "no verification methods to challenge" + try: client = _client(token, publisher_base, ssl_verify) body = CreateChallengesRequest( ctid=ctid, - verification_method_ids=[vm_id], + verification_method_ids=vm_ids, ) response = create_challenges_sync( @@ -229,22 +336,26 @@ def call_create_challenge( ) if response.status_code == 400: - return False, {}, "createChallenges failed" + return False, [], "createChallenges failed" if response.status_code >= 400: - return False, {}, f"createChallenges error {response.status_code}" + return False, [], f"createChallenges error {response.status_code}" data = response.parsed if not data: - return False, {}, "createChallenges returned empty" + return False, [], "createChallenges returned empty" if isinstance(data, list): - first = data[0] - return True, first.to_dict() if hasattr(first, "to_dict") else first, "" + items = [c.to_dict() if hasattr(c, "to_dict") else dict(c) for c in data] + return True, items, "" - return True, {}, "" + single = data.to_dict() if hasattr(data, "to_dict") else None + if isinstance(single, dict): + return True, [single], "" + + return False, [], "createChallenges returned unexpected shape" except Exception as e: - return False, {}, f"createChallenges request failed: {e}" + return False, [], f"createChallenges request failed: {e}" def call_verify_jwt_signature( @@ -320,26 +431,36 @@ def call_submit_to_iir( valid_from: str = "", valid_until: str = "", ) -> tuple[bool, str]: + from datetime import datetime + + def _to_dt_or_unset(value: str): + if not value: + return UNSET + try: + return datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError: + try: + return datetime.strptime(value, "%Y-%m-%d") + except ValueError: + return UNSET + try: client = _client(token, publisher_base, ssl_verify) - body = IssuerDTO( + body = PubIssuerDTO( ctid=ctid, did=did, name=name, legal_name=legal_name, credential_registry_uri=registry_uri, subject_web_page=subject_web_page, - logo_base_64=logo_base64 or None, - logo_uri=logo_uri or None, - valid_from=valid_from or None, - valid_until=valid_until or None, + logo_base_64=logo_base64 if logo_base64 else UNSET, + logo_uri=logo_uri if logo_uri else UNSET, + valid_from=_to_dt_or_unset(valid_from), + valid_until=_to_dt_or_unset(valid_until), ) - response = submit_to_iir_sync( - client=client, - body=body, - ) + response = submit_to_iir_sync(client=client, body=body) if response.status_code == 409: return False, "Did already exists" @@ -352,8 +473,7 @@ def call_submit_to_iir( except Exception as e: return False, f"submitToIIR request failed: {e}" - - + def _parse_uvarint(data: bytes) -> tuple[int, int]: x, s = 0, 0 for i, b in enumerate(data): diff --git a/ce/iir/openapi.json b/ce/iir/openapi.json index 6c45a80..fb828c3 100644 --- a/ce/iir/openapi.json +++ b/ce/iir/openapi.json @@ -1,862 +1,955 @@ { - "openapi": "3.0.0", - "info": { - "version": "v1", - "title": "CTI Directory API" - }, - "paths": { - "/api/iir/IsUserAMemberOfOrg": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_IsUserAMemberOfOrg", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } - } + "openapi": "3.0.0", + "info": { + "version": "v1", + "title": "CTI Directory API" + }, + "paths": { + "/api/iir/IsUserAMemberOfOrg": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_IsUserAMemberOfOrg", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" } - }, - "/api/iir/isUserAMember/{userId}": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_IsUserAMember", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } - } + }, + { + "name": "task", + "in": "query", + "required": true, + "schema": { + "type": "string" } - }, - "/api/iir/getRegistryResource": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetRegistryResource", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/validateDidWeb": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateWeb", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/isUserAMember/{userId}": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_IsUserAMember", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "task", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/validateDidKey": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateKey", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "alg", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/getOrganizationIIRDetail": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_GetOrganizationIIRDetail", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/createChallenges": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_CreateChallenges", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/getRegistryResource": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_GetRegistryResource", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" } + } } - }, - "/api/iir/validateJwt": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateJwtSignature", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/validateDidWeb": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_ValidateWeb", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/saveChallengeToken": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_SaveChallengeToken", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/validateDidKey": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_ValidateKey", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "alg", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/api/iir/createChallenges": { + "post": { + "tags": ["IIRApi"], + "operationId": "IIRApi_CreateChallenges", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } } + }, + "required": true }, - "/api/iir/submitToIIR": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_SubmitToIIR", - "requestBody": { - "$ref": "#/components/requestBodies/IssuerDTO" - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } + } + } + } + }, + "/api/iir/validateJwt": { + "post": { + "tags": ["IIRApi"], + "operationId": "IIRApi_ValidateJwtSignature", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + } + }, + "required": true }, - "/api/iir/issuers": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuers", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/api/iir/saveChallengeToken": { + "post": { + "tags": ["IIRApi"], + "operationId": "IIRApi_SaveChallengeToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } } + }, + "required": true }, - "/api/iir/users/{userId}/issuers": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuersByUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } + } + } + } + }, + "/api/iir/submitToIIR": { + "post": { + "tags": ["IIRApi"], + "operationId": "IIRApi_SubmitToIIR", + "requestBody": { + "$ref": "#/components/requestBodies/PubIssuerDTO" }, - "/api/iir/federationFetch": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_FederationFetch", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "sub", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/issuerByDid": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuerByDid", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/issuers": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_GetIssuers", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "task", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } - }, - "/api/iir/updateIssuer": { - "put": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_UpdateIssuer", - "requestBody": { - "$ref": "#/components/requestBodies/IssuerDTO" - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } + } + } + } + }, + "/api/iir/users/{userId}/issuers": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_GetIssuersByUser", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "task", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } } + } } + } }, - "servers": [ - { - "url": "https://localhost:44330" + "/api/iir/federationFetch": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_FederationFetch", + "parameters": [ + { + "name": "sub", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - ], - "components": { - "requestBodies": { - "IssuerDTO": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IssuerDTO" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/IssuerDTO" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/IssuerDTO" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/IssuerDTO" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/IssuerDTO" - } - } - }, - "required": true + } + }, + "/api/iir/issuerByDid": { + "get": { + "tags": ["IIRApi"], + "operationId": "IIRApi_GetIssuerByDid", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" } - }, - "schemas": { - "CancellationToken": { - "type": "object", - "properties": { - "IsCancellationRequested": { - "type": "boolean", - "readOnly": true - }, - "CanBeCanceled": { - "type": "boolean", - "readOnly": true - }, - "WaitHandle": { - "$ref": "#/components/schemas/WaitHandle" - } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } - }, - "WaitHandle": { - "type": "object", - "properties": { - "Handle": { - "type": "object" - }, - "SafeWaitHandle": { - "$ref": "#/components/schemas/SafeWaitHandle" - } + }, + "text/json": { + "schema": { + "type": "object" } - }, - "SafeWaitHandle": { - "type": "object", - "properties": { - "IsInvalid": { - "type": "boolean", - "readOnly": true - }, - "IsClosed": { - "type": "boolean", - "readOnly": true - } + }, + "application/xml": { + "schema": { + "type": "object" } - }, - "CreateChallengesRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "VerificationMethodIds": { - "type": "array", - "items": { - "type": "string" - } - } + }, + "text/xml": { + "schema": { + "type": "object" } - }, - "ValidateJwtRequest": { - "type": "object", - "properties": { - "Jwt": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - } + } + } + } + } + } + }, + "/api/iir/updateIssuer": { + "put": { + "tags": ["IIRApi"], + "operationId": "IIRApi_UpdateIssuer", + "requestBody": { + "$ref": "#/components/requestBodies/PubIssuerDTO" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" } - }, - "SaveChallengeTokenRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - }, - "Token": { - "type": "string" - } + }, + "text/json": { + "schema": { + "type": "object" } - }, - "IssuerDTO": { - "type": "object", - "properties": { - "CTID": { - "type": "string" - }, - "DID": { - "type": "string" - }, - "Name": { - "type": "string" - }, - "LegalName": { - "type": "string" - }, - "CredentialRegistryUri": { - "type": "string" - }, - "SubjectWebPage": { - "type": "string" - }, - "LogoBase64": { - "type": "string" - }, - "LogoUri": { - "type": "string" - }, - "ValidFrom": { - "format": "date-time", - "type": "string" - }, - "ValidUntil": { - "format": "date-time", - "type": "string" - } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" } + } } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:44330" + } + ], + "components": { + "requestBodies": { + "PubIssuerDTO": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + } + }, + "required": true + } + }, + "schemas": { + "CancellationToken": { + "type": "object", + "properties": { + "IsCancellationRequested": { + "type": "boolean", + "readOnly": true + }, + "CanBeCanceled": { + "type": "boolean", + "readOnly": true + }, + "WaitHandle": { + "$ref": "#/components/schemas/WaitHandle" + } + } + }, + "WaitHandle": { + "type": "object", + "properties": { + "Handle": { + "type": "object" + }, + "SafeWaitHandle": { + "$ref": "#/components/schemas/SafeWaitHandle" + } + } + }, + "SafeWaitHandle": { + "type": "object", + "properties": { + "IsInvalid": { + "type": "boolean", + "readOnly": true + }, + "IsClosed": { + "type": "boolean", + "readOnly": true + } + } + }, + "CreateChallengesRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "VerificationMethodIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ValidateJwtRequest": { + "type": "object", + "properties": { + "Jwt": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } + } + }, + "SaveChallengeTokenRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + }, + "Token": { + "type": "string" + } + } + }, + "PubIssuerDTO": { + "type": "object", + "properties": { + "CTID": { + "type": "string" + }, + "DID": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "LegalName": { + "type": "string" + }, + "CredentialRegistryUri": { + "type": "string" + }, + "SubjectWebPage": { + "type": "string" + }, + "LogoBase64": { + "type": "string" + }, + "LogoUri": { + "type": "string" + }, + "ValidFrom": { + "format": "date-time", + "type": "string" + }, + "ValidUntil": { + "format": "date-time", + "type": "string" + } } + } } -} \ No newline at end of file + } +} diff --git a/ce/iir/swagger.json b/ce/iir/swagger.json index 5f7d6bb..3441ae5 100644 --- a/ce/iir/swagger.json +++ b/ce/iir/swagger.json @@ -1,511 +1,713 @@ { - "swagger": "2.0", - "info": { "version": "v1", "title": "CTI Directory API" }, - "host": "localhost:44330", - "schemes": ["https"], - "paths": { - "/api/iir/IsUserAMemberOfOrg": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_IsUserAMemberOfOrg", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + "swagger": "2.0", + "info": { + "version": "v1", + "title": "CTI Directory API" + }, + "host": "localhost:44330", + "schemes": [ + "https" + ], + "paths": { + "/api/iir/IsUserAMemberOfOrg": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMemberOfOrg", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/isUserAMember/{userId}": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_IsUserAMember", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string", - "format": "uuid" - }, - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/isUserAMember/{userId}": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMember", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "task", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/getRegistryResource": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetRegistryResource", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/getOrganizationIIRDetail": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetOrganizationIIRDetail", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/validateDidWeb": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateWeb", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/getRegistryResource": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetRegistryResource", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/validateDidKey": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateKey", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "alg", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/validateDidWeb": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateWeb", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/createChallenges": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_CreateChallenges", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateChallengesRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/validateDidKey": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateKey", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "alg", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/validateJwt": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateJwtSignature", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { "$ref": "#/definitions/ValidateJwtRequest" } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/createChallenges": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_CreateChallenges", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CreateChallengesRequest" } - }, - "/api/iir/saveChallengeToken": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_SaveChallengeToken", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SaveChallengeTokenRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/submitToIIR": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_SubmitToIIR", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "dto", - "in": "body", - "required": true, - "schema": { "$ref": "#/definitions/IssuerDTO" } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/validateJwt": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateJwtSignature", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/ValidateJwtRequest" } - }, - "/api/iir/issuers": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuers", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/users/{userId}/issuers": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuersByUser", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string", - "format": "uuid" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/saveChallengeToken": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SaveChallengeToken", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SaveChallengeTokenRequest" } - }, - "/api/iir/federationFetch": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_FederationFetch", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "sub", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } - }, - "/api/iir/issuerByDid": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuerByDid", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + } + } + }, + "/api/iir/submitToIIR": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SubmitToIIR", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "dto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/PubIssuerDTO" } - }, - "/api/iir/updateIssuer": { - "put": { - "tags": ["IIRApi"], - "operationId": "IIRApi_UpdateIssuer", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "dto", - "in": "body", - "required": true, - "schema": { "$ref": "#/definitions/IssuerDTO" } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { "type": "object" } - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + } + }, + "/api/iir/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuers", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "keyword", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } + } } + } }, - "definitions": { - "CancellationToken": { - "type": "object", - "properties": { - "IsCancellationRequested": { - "type": "boolean", - "readOnly": true - }, - "CanBeCanceled": { "type": "boolean", "readOnly": true }, - "WaitHandle": { - "$ref": "#/definitions/WaitHandle", - "readOnly": true - } + "/api/iir/users/{userId}/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuersByUser", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "page", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "keyword", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" } + } + } + } + }, + "/api/iir/federationFetch": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_FederationFetch", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "sub", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + } + }, + "/api/iir/issuerByDid": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuerByDid", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + } + }, + "/api/iir/updateIssuer": { + "put": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_UpdateIssuer", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "dto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/PubIssuerDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + } + } + }, + "definitions": { + "CancellationToken": { + "type": "object", + "properties": { + "IsCancellationRequested": { + "type": "boolean", + "readOnly": true + }, + "CanBeCanceled": { + "type": "boolean", + "readOnly": true }, "WaitHandle": { - "type": "object", - "properties": { - "Handle": { "type": "object" }, - "SafeWaitHandle": { "$ref": "#/definitions/SafeWaitHandle" } - } + "$ref": "#/definitions/WaitHandle", + "readOnly": true + } + } + }, + "WaitHandle": { + "type": "object", + "properties": { + "Handle": { + "type": "object" }, "SafeWaitHandle": { - "type": "object", - "properties": { - "IsInvalid": { "type": "boolean", "readOnly": true }, - "IsClosed": { "type": "boolean", "readOnly": true } - } + "$ref": "#/definitions/SafeWaitHandle" + } + } + }, + "SafeWaitHandle": { + "type": "object", + "properties": { + "IsInvalid": { + "type": "boolean", + "readOnly": true }, - "CreateChallengesRequest": { - "type": "object", - "properties": { - "Ctid": { "type": "string" }, - "VerificationMethodIds": { - "type": "array", - "items": { "type": "string" } - } - } + "IsClosed": { + "type": "boolean", + "readOnly": true + } + } + }, + "CreateChallengesRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" }, - "ValidateJwtRequest": { - "type": "object", - "properties": { - "Jwt": { "type": "string" }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - } - } + "VerificationMethodIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ValidateJwtRequest": { + "type": "object", + "properties": { + "Jwt": { + "type": "string" }, - "SaveChallengeTokenRequest": { - "type": "object", - "properties": { - "Ctid": { "type": "string" }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - }, - "Token": { "type": "string" } - } + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } + } + }, + "SaveChallengeTokenRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" }, - "IssuerDTO": { - "type": "object", - "properties": { - "CTID": { "type": "string" }, - "DID": { "type": "string" }, - "Name": { "type": "string" }, - "LegalName": { "type": "string" }, - "CredentialRegistryUri": { "type": "string" }, - "SubjectWebPage": { "type": "string" }, - "LogoBase64": { "type": "string" }, - "LogoUri": { "type": "string" }, - "ValidFrom": { "format": "date-time", "type": "string" }, - "ValidUntil": { "format": "date-time", "type": "string" } - } + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + }, + "Token": { + "type": "string" + } + } + }, + "PubIssuerDTO": { + "type": "object", + "properties": { + "CTID": { + "type": "string" + }, + "DID": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "LegalName": { + "type": "string" + }, + "CredentialRegistryUri": { + "type": "string" + }, + "SubjectWebPage": { + "type": "string" + }, + "LogoBase64": { + "type": "string" + }, + "LogoUri": { + "type": "string" + }, + "ValidFrom": { + "format": "date-time", + "type": "string" + }, + "ValidUntil": { + "format": "date-time", + "type": "string" } + } } -} + } +} \ No newline at end of file diff --git a/generate.ps1 b/generate.ps1 index 72b9f8b..b9d98af 100644 --- a/generate.ps1 +++ b/generate.ps1 @@ -1,5 +1,8 @@ +npm install -g swagger2openapi pip install openapi-python-client --user +swagger2openapi ce/iir/swagger.json -o ce/iir/openapi.json + python -m openapi_python_client generate ` --path ce/iir/openapi.json ` --config ce/iir/api/openapi-client-config.yml ` From 8bd9b09bd42208c585b8a8d4d50739689c3a0868 Mon Sep 17 00:00:00 2001 From: Sneha Edula Date: Mon, 4 May 2026 15:29:54 -0400 Subject: [PATCH 2/3] Update Tests --- tests/test_cli.py | 48 ++-- tests/test_did_ops_integration.py | 53 +++- tests/test_did_ops_unit.py | 397 ++++++++++++++++++++++++++---- 3 files changed, 413 insertions(+), 85 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 54f6790..ff5c880 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -23,7 +23,7 @@ _did_web_to_url, _extract_ed25519_pub_from_did_key, _parse_uvarint, - call_create_challenge, + call_create_challenges, call_get_registry_resource, call_is_user_member, call_save_challenge_token, @@ -322,45 +322,45 @@ def test_502_means_remote_did_document_unreachable(self): assert "502" in err -class TestCallCreateChallenge: +class TestCallCreateChallenges: def _challenge_body(self): return _ParsedObj( - challenge=CHALLENGE_UUID, + Challenge=CHALLENGE_UUID, Did=ED25519_DID_KEY, Aud="ce-api", Iat=1700000000, Exp=1700003600, ) - def test_returns_first_challenge_on_success(self): + def test_returns_list_on_success(self): with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response(200, [self._challenge_body()]) - ok, data, err = call_create_challenge( - CTID, ED25519_KID, ACCESS_TOKEN, PUBLISHER_BASE, False + ok, data, err = call_create_challenges( + CTID, [ED25519_KID], ACCESS_TOKEN, PUBLISHER_BASE, False ) assert ok and err == "" - assert data["challenge"] == CHALLENGE_UUID + assert isinstance(data, list) + assert len(data) == 1 + assert data[0]["Challenge"] == CHALLENGE_UUID def test_empty_response_list_is_an_error(self): - # An empty list is falsy -> `if not data` fires -> "returned empty" message. with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response(200, []) - ok, data, err = call_create_challenge( - CTID, ED25519_KID, ACCESS_TOKEN, PUBLISHER_BASE, False + ok, data, err = call_create_challenges( + CTID, [ED25519_KID], ACCESS_TOKEN, PUBLISHER_BASE, False ) - assert not ok and data == {} + assert not ok and data == [] assert "empty" in err def test_400_returns_generic_failure_message(self): with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response(400, None) - ok, data, err = call_create_challenge( - CTID, "bad-vm", ACCESS_TOKEN, PUBLISHER_BASE, False + ok, data, err = call_create_challenges( + CTID, ["bad-vm"], ACCESS_TOKEN, PUBLISHER_BASE, False ) - assert not ok and data == {} + assert not ok and data == [] assert err == "createChallenges failed" - class TestCallVerifyJwtSignature: def test_success(self): with patch("ce.iir.did_ops.validate_jwt_sync") as mock_sync: @@ -427,6 +427,7 @@ def test_409_means_did_already_registered(self): assert not ok and err == "Did already exists" def test_valid_from_and_until_are_passed_as_model_fields(self): + from datetime import datetime, timezone with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: mock_sync.return_value = _fake_response(200, None) call_submit_to_iir( @@ -436,19 +437,22 @@ def test_valid_from_and_until_are_passed_as_model_fields(self): ) _, kwargs = mock_sync.call_args body = kwargs["body"] - assert body.valid_from == "2024-01-01T00:00:00Z" - assert body.valid_until == "2025-01-01T00:00:00Z" + assert isinstance(body.valid_from, datetime) + assert isinstance(body.valid_until, datetime) + assert body.valid_from == datetime(2024, 1, 1, tzinfo=timezone.utc) + assert body.valid_until == datetime(2025, 1, 1, tzinfo=timezone.utc) - def test_empty_optional_fields_become_none_on_model(self): + def test_empty_optional_fields_are_unset_not_none(self): + from ce.iir.api.iir_client.types import UNSET with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: mock_sync.return_value = _fake_response(200, None) call_submit_to_iir(*self._base_args()) _, kwargs = mock_sync.call_args body = kwargs["body"] - assert body.logo_uri is None - assert body.logo_base_64 is None - assert body.valid_from is None - assert body.valid_until is None + assert body.logo_uri is UNSET + assert body.logo_base_64 is UNSET + assert body.valid_from is UNSET + assert body.valid_until is UNSET class TestHelpers: diff --git a/tests/test_did_ops_integration.py b/tests/test_did_ops_integration.py index f0f9fde..1cbaf4c 100644 --- a/tests/test_did_ops_integration.py +++ b/tests/test_did_ops_integration.py @@ -20,7 +20,8 @@ import requests as req from ce.iir.did_ops import ( - call_create_challenge, + call_create_challenges, + call_get_organization_iir_detail, call_get_registry_resource, call_is_user_member, call_save_challenge_token, @@ -79,8 +80,8 @@ def _require_env(): def server_available(): """Skip everything if the API server isn't reachable.""" try: - req.get(f"{PUBLISHER_BASE}/api/iir/isUserAMember", timeout=5, verify=False) - except req.exceptions.ConnectionError: + req.get(f"{PUBLISHER_BASE}/api/iir", timeout=5, verify=False) + except (req.exceptions.ConnectionError, req.exceptions.Timeout): pytest.skip(f"API server not reachable at {PUBLISHER_BASE}") @@ -151,6 +152,28 @@ def test_unknown_ctid_not_found(self, access_token): assert err +class TestGetOrganizationIIRDetailIntegration: + + def test_known_ctid_returns_org_detail_or_skips(self, access_token): + ok, data, err = call_get_organization_iir_detail( + TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False + ) + if not ok: + pytest.skip(f"Org has no Accounts detail yet: {err}") + + assert data.get("LegalName") or data.get("LogoUrl"), ( + "getOrganizationIIRDetail returned valid=true but data was empty" + ) + + def test_unknown_ctid_returns_failure(self, access_token): + ok, _, err = call_get_organization_iir_detail( + "ce-00000000-0000-0000-0000-999999999999", + access_token, PUBLISHER_BASE, ssl_verify=False, + ) + assert not ok + assert err + + class TestValidateDidKeyIntegration: def test_valid_did_key_returns_vm_ids(self, access_token): ok, data, err = call_validate_did_key( @@ -168,6 +191,8 @@ def test_malformed_did_key_returns_error(self, access_token): class TestValidateDidWebIntegration: def test_valid_did_web_resolves(self, access_token): + if not TEST_DID_WEB: + pytest.skip("CE_TEST_DID_WEB not set — skipping did:web integration test.") ok, data, err = call_validate_did_web( TEST_DID_WEB, access_token, PUBLISHER_BASE, ssl_verify=False ) @@ -183,19 +208,29 @@ def test_nonexistent_domain_returns_error(self, access_token): class TestFullChallengeFlowIntegration: def test_create_challenge(self, access_token): - ok, challenge, err = call_create_challenge( - TEST_CTID, TEST_VM, access_token, PUBLISHER_BASE, ssl_verify=False + ok, challenges, err = call_create_challenges( + TEST_CTID, [TEST_VM], access_token, PUBLISHER_BASE, ssl_verify=False ) assert ok, f"createChallenges failed: {err}" - assert challenge.get("challenge") or challenge.get("Challenge") + assert isinstance(challenges, list) and challenges + first = challenges[0] + assert first.get("Challenge") or first.get("challenge") def test_full_sign_verify_save_flow(self, access_token, private_key): - ok, challenge, err = call_create_challenge( - TEST_CTID, TEST_VM, access_token, PUBLISHER_BASE, ssl_verify=False + ok, challenges, err = call_create_challenges( + TEST_CTID, [TEST_VM], access_token, PUBLISHER_BASE, ssl_verify=False ) assert ok, f"createChallenges failed: {err}" + assert challenges, "createChallenges returned empty list" + + challenge = next( + (c for c in challenges if (c.get("Did") or c.get("did")) == TEST_VM), + challenges[0], + ) + + challenge_uuid = challenge.get("Challenge") or challenge.get("challenge", "") + assert challenge_uuid, "Challenge UUID missing from response" - challenge_uuid = challenge.get("challenge") or challenge.get("Challenge", "") payload = { "did": challenge.get("Did") or challenge.get("did", TEST_DID_KEY), "challenge": challenge_uuid, diff --git a/tests/test_did_ops_unit.py b/tests/test_did_ops_unit.py index 7ec752d..bf96099 100644 --- a/tests/test_did_ops_unit.py +++ b/tests/test_did_ops_unit.py @@ -8,9 +8,9 @@ import os import base64 import json -import sys +from datetime import datetime, timezone from types import SimpleNamespace -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest @@ -19,28 +19,36 @@ import ce.iir.did_ops as did_ops from ce.iir.did_ops import ( + IIR_TASK, _client, _did_web_to_url, _extract_ed25519_pub_from_did_key, _parse_uvarint, - call_create_challenge, + _parsed_to_dict, + call_create_challenges, + call_get_organization_iir_detail, call_get_registry_resource, call_is_user_member, call_save_challenge_token, call_submit_to_iir, + call_validate_did, call_validate_did_key, call_validate_did_web, call_verify_jwt_signature, classify_did, extract_user_id_from_token, + merge_autofill, sign_proof_jwt, validate_ctid, validate_date, + validate_date_range, validate_did_key_prefix, validate_verification_method, verify_proof_jwt, ) +from ce.iir.api.iir_client.types import UNSET + PUBLISHER_BASE = os.getenv("CE_PUBLISHER_BASE") ACCESS_TOKEN = os.getenv("CE_TEST_ACCESS_TOKEN") @@ -50,7 +58,7 @@ ED25519_DID_KEY = os.getenv("CE_TEST_DID_KEY") ED25519_KID = ( f"{ED25519_DID_KEY}#{ED25519_DID_KEY[len('did:key:'):]}" - if ED25519_DID_KEY.startswith("did:key:") + if ED25519_DID_KEY and ED25519_DID_KEY.startswith("did:key:") else "" ) @@ -69,9 +77,6 @@ class _ParsedObj: def __init__(self, **kwargs): self._data = kwargs - # FIX 1: added so call_is_user_member / call_get_registry_resource can - # read the payload — without this, getattr(parsed, "additional_properties", {}) - # always returned {}, causing every _ParsedObj-based test to fail. @property def additional_properties(self): return self._data @@ -88,6 +93,28 @@ def test_client_strips_trailing_slash(self): assert client._verify_ssl is False +class TestParsedToDict: + """_parsed_to_dict centralises SDK response unwrapping; verify all 3 shapes.""" + + def test_dict_passthrough(self): + assert _parsed_to_dict({"valid": True}) == {"valid": True} + + def test_object_with_to_dict(self): + assert _parsed_to_dict(_ParsedObj(valid=True, name="x")) == {"valid": True, "name": "x"} + + def test_object_with_only_additional_properties(self): + class Bare: + additional_properties = {"valid": False} + + assert _parsed_to_dict(Bare()) == {"valid": False} + + def test_none_returns_empty_dict(self): + assert _parsed_to_dict(None) == {} + + def test_falsy_returns_empty_dict(self): + assert _parsed_to_dict({}) == {} + + class TestValidateCtid: def test_valid_ctid_passes(self): ok, err = validate_ctid(CTID) @@ -132,6 +159,42 @@ def test_impossible_date_fails(self): assert "MM/DD/YYYY" in err +class TestValidateDateRange: + + def test_both_empty_is_ok(self): + ok, err = validate_date_range("", "") + assert ok and err == "" + + def test_only_from_provided_fails(self): + ok, err = validate_date_range("01/01/2024", "") + assert not ok + assert "both" in err.lower() + + def test_only_until_provided_fails(self): + ok, err = validate_date_range("", "01/01/2024") + assert not ok + assert "both" in err.lower() + + def test_until_before_from_fails(self): + ok, err = validate_date_range("06/01/2024", "01/01/2024") + assert not ok + assert "after" in err.lower() + + def test_until_equal_to_from_fails(self): + ok, err = validate_date_range("01/01/2024", "01/01/2024") + assert not ok + assert "after" in err.lower() + + def test_until_after_from_passes(self): + ok, err = validate_date_range("01/01/2024", "01/02/2024") + assert ok and err == "" + + def test_invalid_format_propagates_error(self): + ok, err = validate_date_range("not-a-date", "01/01/2024") + assert not ok + assert "MM/DD/YYYY" in err + + class TestClassifyDid: def test_did_key_recognised(self): kind, err = classify_did("did:key:z6Mk...") @@ -214,16 +277,11 @@ def test_returns_true_when_valid_dict_payload(self): assert ok and err == "" def test_object_payload_with_valid_true_succeeds(self): - # _ParsedObj.additional_properties returns {"valid": True}, which the - # implementation reads via getattr(parsed, "additional_properties", {}). with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: mock_sync.return_value = _fake_response(200, _ParsedObj(valid=True)) ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) assert ok and err == "" - # FIX 2: was "test_current_implementation_treats_object_payload_as_success" - # and incorrectly asserted ok=True. With additional_properties present, - # valid=False is now correctly read, so the call returns (False, "not a member"). def test_object_payload_with_valid_false_fails(self): with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: mock_sync.return_value = _fake_response(200, _ParsedObj(valid=False)) @@ -231,9 +289,6 @@ def test_object_payload_with_valid_false_fails(self): assert not ok assert "not a member" in err.lower() - # FIX 3: was "test_propagates_http_error_status" asserting "403" in err. - # call_is_user_member has no status-code branch — a 403 with parsed=None - # gives data={}, valid=False, and returns the generic "not a member" message. def test_http_error_yields_not_member_message(self): with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: mock_sync.return_value = _fake_response(403, None) @@ -246,6 +301,24 @@ def test_handles_exception_gracefully(self): ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) assert not ok and "timeout" in err + def test_default_task_is_iir(self): + with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, {"valid": True}) + call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) + args, kwargs = mock_sync.call_args + assert kwargs["task"] == "IIR" + assert kwargs["task"] == IIR_TASK + assert kwargs["ctid"] == CTID + + def test_explicit_task_overrides_default(self): + with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, {"valid": True}) + call_is_user_member( + USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False, task="OTHER" + ) + _, kwargs = mock_sync.call_args + assert kwargs["task"] == "OTHER" + class TestCallGetRegistryResource: def test_returns_data_when_ctid_exists(self): @@ -256,8 +329,6 @@ def test_returns_data_when_ctid_exists(self): assert ok and err == "" assert data["Name"] == "Acme" - # FIX 4: was asserting data == {}. The implementation returns the parsed dict - # as-is on ExistsInRegistry=False, not an empty dict. def test_returns_false_when_ctid_not_in_registry_dict_payload(self): with patch("ce.iir.did_ops.get_registry_resource_sync") as mock_sync: mock_sync.return_value = _fake_response(200, {"ExistsInRegistry": False}) @@ -266,9 +337,6 @@ def test_returns_false_when_ctid_not_in_registry_dict_payload(self): assert data == {"ExistsInRegistry": False} assert "not found" in err - # FIX 5: was asserting err == "Registry lookup failed". There is no status-code - # branch in call_get_registry_resource — a 400 with parsed=None gives data={}, - # ExistsInRegistry missing → exists=False → returns the "not found" message. def test_400_falls_through_to_not_found_message(self): with patch("ce.iir.did_ops.get_registry_resource_sync") as mock_sync: mock_sync.return_value = _fake_response(400, None) @@ -278,23 +346,134 @@ def test_400_falls_through_to_not_found_message(self): def test_exception_returns_api_request_failed_message(self): with patch( - "ce.iir.did_ops.get_registry_resource_sync", side_effect=RuntimeError("conn refused") + "ce.iir.did_ops.get_registry_resource_sync", + side_effect=RuntimeError("conn refused"), ): ok, data, err = call_get_registry_resource(CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) assert not ok and data == {} assert "conn refused" in err +class TestCallGetOrganizationIIRDetail: + + def test_unwraps_envelope_on_success(self): + body = _ParsedObj( + valid=True, + data={"LegalName": "Acme Corp", "LogoUrl": "https://acme/logo.png"}, + ) + with patch("ce.iir.did_ops.get_organization_iir_detail_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, body) + ok, data, err = call_get_organization_iir_detail( + CTID, ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert ok and err == "" + assert data["LegalName"] == "Acme Corp" + assert data["LogoUrl"] == "https://acme/logo.png" + + def test_valid_false_returns_failure(self): + with patch("ce.iir.did_ops.get_organization_iir_detail_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, {"valid": False}) + ok, data, err = call_get_organization_iir_detail( + CTID, ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok + assert "not available" in err.lower() + + def test_empty_envelope_returns_failure(self): + with patch("ce.iir.did_ops.get_organization_iir_detail_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, None) + ok, data, err = call_get_organization_iir_detail( + CTID, ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok and data == {} + assert "empty" in err.lower() + + def test_missing_data_key_returns_empty_dict(self): + with patch("ce.iir.did_ops.get_organization_iir_detail_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, {"valid": True}) + ok, data, err = call_get_organization_iir_detail( + CTID, ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert ok and err == "" + assert data == {} + + def test_exception_returns_failure(self): + with patch( + "ce.iir.did_ops.get_organization_iir_detail_sync", + side_effect=RuntimeError("network error"), + ): + ok, data, err = call_get_organization_iir_detail( + CTID, ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok and data == {} + assert "network error" in err + + +class TestMergeAutofill: + def test_accounts_legal_name_overrides_registry(self): + registry = {"Name": "Acme", "LegalName": "Acme Inc.", "Image": "logo.png"} + accounts = {"LegalName": "Acme Corporation"} + merged = merge_autofill(registry, accounts) + assert merged["legal_name"] == "Acme Corporation" + assert merged["name"] == "Acme" + assert merged["image"] == "logo.png" + + def test_accounts_logo_only_fills_when_registry_image_empty(self): + registry = {"Name": "Acme", "LegalName": "", "Image": "registry-logo.png"} + accounts = {"LegalName": "Acme Corp", "LogoUrl": "accounts-logo.png"} + merged = merge_autofill(registry, accounts) + assert merged["image"] == "registry-logo.png" + + def test_accounts_logo_used_when_registry_image_blank(self): + registry = {"Name": "Acme", "LegalName": "", "Image": ""} + accounts = {"LegalName": "Acme Corp", "LogoUrl": "accounts-logo.png"} + merged = merge_autofill(registry, accounts) + assert merged["image"] == "accounts-logo.png" + + def test_no_accounts_data_falls_back_to_registry(self): + registry = { + "Name": "Acme", + "LegalName": "Acme Inc.", + "Image": "logo.png", + "CredentialRegistryUri": "https://registry/x", + "SubjectWebpage": "https://acme.example", + "LogoBase64": "abc", + } + merged = merge_autofill(registry, {}) + assert merged["legal_name"] == "Acme Inc." + assert merged["image"] == "logo.png" + assert merged["registry_uri"] == "https://registry/x" + assert merged["subject_webpage"] == "https://acme.example" + assert merged["logo_base64"] == "abc" + + def test_empty_accounts_legal_name_does_not_clear_registry(self): + registry = {"Name": "Acme", "LegalName": "Acme Inc."} + accounts = {"LegalName": ""} + merged = merge_autofill(registry, accounts) + assert merged["legal_name"] == "Acme Inc." + + def test_handles_missing_registry_keys_gracefully(self): + merged = merge_autofill({}, {}) + assert merged == { + "name": "", + "legal_name": "", + "registry_uri": "", + "subject_webpage": "", + "image": "", + "logo_base64": "", + } + + class TestCallValidateDidKey: def test_returns_verification_method_ids_on_success(self): - body = _ParsedObj(verificationMethodIds=[ED25519_KID]) + body = _ParsedObj(VerificationMethodIds=[ED25519_KID]) with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: mock_sync.return_value = _fake_response(200, body) ok, data, err = call_validate_did_key( ED25519_DID_KEY, "Ed25519", ACCESS_TOKEN, PUBLISHER_BASE, False ) assert ok and err == "" - assert ED25519_KID in data["verificationMethodIds"] + assert ED25519_KID in data["VerificationMethodIds"] def test_400_returns_generic_failure_message(self): with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: @@ -308,14 +487,14 @@ def test_400_returns_generic_failure_message(self): class TestCallValidateDidWeb: def test_success(self): - body = _ParsedObj(verificationMethodIds=["did:web:example.com#key-1"]) + body = _ParsedObj(VerificationMethodIds=["did:web:example.com#key-1"]) with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: mock_sync.return_value = _fake_response(200, body) ok, data, err = call_validate_did_web( "did:web:example.com", ACCESS_TOKEN, PUBLISHER_BASE, False ) assert ok and err == "" - assert data["verificationMethodIds"] == ["did:web:example.com#key-1"] + assert data["VerificationMethodIds"] == ["did:web:example.com#key-1"] def test_502_means_remote_did_document_unreachable(self): with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: @@ -327,42 +506,118 @@ def test_502_means_remote_did_document_unreachable(self): assert "502" in err -class TestCallCreateChallenge: - def _challenge_body(self): +class TestCallValidateDid: + def test_did_key_path_returns_vm_ids(self): + body = _ParsedObj(VerificationMethodIds=[ED25519_KID]) + with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, body) + ok, vm_ids, data, err = call_validate_did( + ED25519_DID_KEY, "Ed25519", ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert ok and err == "" + assert vm_ids == [ED25519_KID] + assert data["VerificationMethodIds"] == [ED25519_KID] + + def test_did_web_path_returns_vm_ids(self): + body = _ParsedObj(VerificationMethodIds=["did:web:example.com#key-1"]) + with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, body) + ok, vm_ids, data, err = call_validate_did( + "did:web:example.com", "", ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert ok and err == "" + assert vm_ids == ["did:web:example.com#key-1"] + + def test_unsupported_did_method_returns_classify_error(self): + ok, vm_ids, data, err = call_validate_did( + "did:ethr:0x123", "", ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok + assert vm_ids == [] + assert "unrecognised" in err + + def test_did_key_with_bad_prefix_short_circuits(self): + with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: + ok, vm_ids, _, err = call_validate_did( + "did:key:zXXXunknown", "Ed25519", ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok and vm_ids == [] + assert "unrecognised" in err.lower() + mock_sync.assert_not_called() + + def test_missing_vm_ids_returns_empty_list_not_none(self): + with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, _ParsedObj(somethingElse=True)) + ok, vm_ids, _, err = call_validate_did( + "did:web:example.com", "", ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert ok and err == "" + assert vm_ids == [] + + +class TestCallCreateChallenges: + """Batch challenges: takes a list of vm_ids, returns a list of challenge dicts.""" + + def _challenge_body(self, did=None, challenge=None): return _ParsedObj( - challenge=CHALLENGE_UUID, - Did=ED25519_DID_KEY, + Challenge=challenge or CHALLENGE_UUID, + Did=did or ED25519_DID_KEY, Aud="ce-api", Iat=1700000000, Exp=1700003600, ) - def test_returns_first_challenge_on_success(self): + def test_returns_list_on_success(self): with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, [self._challenge_body()]) - ok, data, err = call_create_challenge( - CTID, ED25519_KID, ACCESS_TOKEN, PUBLISHER_BASE, False + mock_sync.return_value = _fake_response( + 200, [self._challenge_body(did=f"{ED25519_DID_KEY}#k1"), + self._challenge_body(did=f"{ED25519_DID_KEY}#k2")] + ) + ok, data, err = call_create_challenges( + CTID, [f"{ED25519_DID_KEY}#k1", f"{ED25519_DID_KEY}#k2"], + ACCESS_TOKEN, PUBLISHER_BASE, False, ) assert ok and err == "" - assert data["challenge"] == CHALLENGE_UUID + assert isinstance(data, list) + assert len(data) == 2 + assert all(c["Challenge"] == CHALLENGE_UUID for c in data) + + def test_passes_all_vm_ids_in_single_request(self): + vm_ids = [f"{ED25519_DID_KEY}#k1", f"{ED25519_DID_KEY}#k2"] + with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, [self._challenge_body()]) + call_create_challenges(CTID, vm_ids, ACCESS_TOKEN, PUBLISHER_BASE, False) + _, kwargs = mock_sync.call_args + body = kwargs["body"] + assert mock_sync.call_count == 1 + assert body.verification_method_ids == vm_ids + assert body.ctid == CTID + + def test_empty_vm_ids_short_circuits_without_api_call(self): + with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: + ok, data, err = call_create_challenges( + CTID, [], ACCESS_TOKEN, PUBLISHER_BASE, False + ) + assert not ok and data == [] + assert "no verification methods" in err.lower() + mock_sync.assert_not_called() def test_empty_response_list_is_an_error(self): - # An empty list is falsy -> `if not data` fires -> "returned empty" message. with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response(200, []) - ok, data, err = call_create_challenge( - CTID, ED25519_KID, ACCESS_TOKEN, PUBLISHER_BASE, False + ok, data, err = call_create_challenges( + CTID, [ED25519_KID], ACCESS_TOKEN, PUBLISHER_BASE, False ) - assert not ok and data == {} + assert not ok and data == [] assert "empty" in err def test_400_returns_generic_failure_message(self): with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response(400, None) - ok, data, err = call_create_challenge( - CTID, "bad-vm", ACCESS_TOKEN, PUBLISHER_BASE, False + ok, data, err = call_create_challenges( + CTID, ["bad-vm"], ACCESS_TOKEN, PUBLISHER_BASE, False ) - assert not ok and data == {} + assert not ok and data == [] assert err == "createChallenges failed" @@ -431,7 +686,7 @@ def test_409_means_did_already_registered(self): ok, err = call_submit_to_iir(*self._base_args()) assert not ok and err == "Did already exists" - def test_valid_from_and_until_are_passed_as_model_fields(self): + def test_iso_strings_become_datetime_objects_on_dto(self): with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: mock_sync.return_value = _fake_response(200, None) call_submit_to_iir( @@ -441,19 +696,54 @@ def test_valid_from_and_until_are_passed_as_model_fields(self): ) _, kwargs = mock_sync.call_args body = kwargs["body"] - assert body.valid_from == "2024-01-01T00:00:00Z" - assert body.valid_until == "2025-01-01T00:00:00Z" + assert isinstance(body.valid_from, datetime) + assert isinstance(body.valid_until, datetime) + assert body.valid_from == datetime(2024, 1, 1, tzinfo=timezone.utc) + assert body.valid_until == datetime(2025, 1, 1, tzinfo=timezone.utc) + + def test_yyyy_mm_dd_dates_also_parse(self): + with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, None) + call_submit_to_iir( + *self._base_args(), valid_from="2024-06-15", valid_until="2025-06-15" + ) + _, kwargs = mock_sync.call_args + body = kwargs["body"] + assert isinstance(body.valid_from, datetime) + assert body.valid_from.year == 2024 and body.valid_from.month == 6 - def test_empty_optional_fields_become_none_on_model(self): + def test_empty_optional_fields_are_unset_not_none(self): with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: mock_sync.return_value = _fake_response(200, None) call_submit_to_iir(*self._base_args()) _, kwargs = mock_sync.call_args body = kwargs["body"] - assert body.logo_uri is None - assert body.logo_base_64 is None - assert body.valid_from is None - assert body.valid_until is None + assert body.logo_uri is UNSET + assert body.logo_base_64 is UNSET + assert body.valid_from is UNSET + assert body.valid_until is UNSET + + def test_dto_to_dict_does_not_crash_on_empty_optionals(self): + with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, None) + call_submit_to_iir(*self._base_args()) + _, kwargs = mock_sync.call_args + body = kwargs["body"] + d = body.to_dict() + assert "ValidFrom" not in d + assert "ValidUntil" not in d + assert "LogoUri" not in d + assert "LogoBase64" not in d + + def test_invalid_date_string_falls_back_to_unset(self): + with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: + mock_sync.return_value = _fake_response(200, None) + call_submit_to_iir( + *self._base_args(), valid_from="garbage-date", valid_until="" + ) + _, kwargs = mock_sync.call_args + body = kwargs["body"] + assert body.valid_from is UNSET class TestHelpers: @@ -471,7 +761,10 @@ def test_did_web_to_url_without_path(self): assert _did_web_to_url("did:web:example.com") == "https://example.com/.well-known/did.json" def test_did_web_to_url_with_path(self): - assert _did_web_to_url("did:web:example.com:users:alice") == "https://example.com/users/alice/did.json" + assert ( + _did_web_to_url("did:web:example.com:users:alice") + == "https://example.com/users/alice/did.json" + ) @pytest.fixture(scope="session") @@ -510,10 +803,6 @@ def test_sign_and_verify_roundtrip(self, private_key): ok, err = verify_proof_jwt(token, ED25519_KID) assert ok, err - # FIX 6: the old version mutated the last base64url character, which may be - # padding-ignored (it can contribute 0 bits of actual data), so the decoded - # signature bytes were unchanged and verification still passed. Mutating the - # first character always affects the decoded bytes. def test_tampered_signature_fails_verification(self, private_key): token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) parts = token.split(".") From 5e75b507372b1d06a3834c522486e34eacc4e9f8 Mon Sep 17 00:00:00 2001 From: Sneha Edula Date: Thu, 7 May 2026 17:37:17 -0400 Subject: [PATCH 3/3] record and playback tests --- .../api/iir_api/iir_api_create_challenges.py | 2 +- .../api/iir_api/iir_api_federation_fetch.py | 2 +- .../api/iir_api/iir_api_get_issuer_by_did.py | 2 +- .../api/iir_api/iir_api_get_issuers.py | 22 +- .../iir_api/iir_api_get_issuers_by_user.py | 30 +- .../iir_api_get_organization_iir_detail.py | 2 +- .../iir_api/iir_api_get_registry_resource.py | 2 +- .../api/iir_api/iir_api_is_user_a_member.py | 2 +- .../iir_api_is_user_a_member_of_org.py | 2 +- .../iir_api/iir_api_save_challenge_token.py | 2 +- .../api/iir_api/iir_api_submit_to_iir.py | 2 +- .../api/iir_api/iir_api_update_issuer.py | 2 +- .../iir_api/iir_api_validate_jwt_signature.py | 2 +- .../api/iir_api/iir_api_validate_key.py | 2 +- .../api/iir_api/iir_api_validate_web.py | 2 +- ce/iir/csv_processor.py | 19 +- ce/iir/openapi.json | 1848 +++++++++-------- ce/iir/swagger.json | 1364 ++++++------ ...FlowIntegration.test_create_challenge.yaml | 46 + ...ation.test_full_sign_verify_save_flow.yaml | 134 ++ ...nown_ctid_returns_org_detail_or_skips.yaml | 43 + ...ion.test_unknown_ctid_returns_failure.yaml | 43 + ...on.test_known_ctid_exists_in_registry.yaml | 44 + ...tegration.test_unknown_ctid_not_found.yaml | 42 + ...gration.test_bogus_ctid_returns_error.yaml | 42 + ...tion.test_member_check_returns_a_bool.yaml | 42 + ...rIntegration.test_member_of_known_org.yaml | 42 + ...nts_legal_name_overrides_when_present.yaml | 85 + ...est_registry_data_flows_through_merge.yaml | 85 + ...mit_returns_success_or_already_exists.yaml | 46 + ...test_submit_with_logo_uri_is_accepted.yaml | 46 + ...t_submit_with_valid_dates_is_accepted.yaml | 46 + ...rns_verification_method_for_known_did.yaml | 42 + ...rns_verification_method_for_known_did.yaml | 42 + ....test_malformed_did_key_returns_error.yaml | 42 + ...ion.test_valid_did_key_returns_vm_ids.yaml | 42 + ...test_nonexistent_domain_returns_error.yaml | 43 + ...tegration.test_valid_did_web_resolves.yaml | 42 + tests/conftest.py | 41 +- tests/test_cli.py | 547 ----- tests/test_did_ops_integration.py | 289 ++- tests/test_did_ops_unit.py | 150 +- 42 files changed, 3080 insertions(+), 2295 deletions(-) create mode 100644 tests/cassettes/TestFullChallengeFlowIntegration.test_create_challenge.yaml create mode 100644 tests/cassettes/TestFullChallengeFlowIntegration.test_full_sign_verify_save_flow.yaml create mode 100644 tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_known_ctid_returns_org_detail_or_skips.yaml create mode 100644 tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_unknown_ctid_returns_failure.yaml create mode 100644 tests/cassettes/TestGetRegistryResourceIntegration.test_known_ctid_exists_in_registry.yaml create mode 100644 tests/cassettes/TestGetRegistryResourceIntegration.test_unknown_ctid_not_found.yaml create mode 100644 tests/cassettes/TestIsUserMemberIntegration.test_bogus_ctid_returns_error.yaml create mode 100644 tests/cassettes/TestIsUserMemberIntegration.test_member_check_returns_a_bool.yaml create mode 100644 tests/cassettes/TestIsUserMemberIntegration.test_member_of_known_org.yaml create mode 100644 tests/cassettes/TestMergeAutofillEndToEnd.test_accounts_legal_name_overrides_when_present.yaml create mode 100644 tests/cassettes/TestMergeAutofillEndToEnd.test_registry_data_flows_through_merge.yaml create mode 100644 tests/cassettes/TestSubmitToIirIntegration.test_submit_returns_success_or_already_exists.yaml create mode 100644 tests/cassettes/TestSubmitToIirIntegration.test_submit_with_logo_uri_is_accepted.yaml create mode 100644 tests/cassettes/TestSubmitToIirIntegration.test_submit_with_valid_dates_is_accepted.yaml create mode 100644 tests/cassettes/TestValidateDidDispatcherIntegration.test_did_key_returns_verification_method_for_known_did.yaml create mode 100644 tests/cassettes/TestValidateDidDispatcherIntegration.test_did_web_returns_verification_method_for_known_did.yaml create mode 100644 tests/cassettes/TestValidateDidKeyIntegration.test_malformed_did_key_returns_error.yaml create mode 100644 tests/cassettes/TestValidateDidKeyIntegration.test_valid_did_key_returns_vm_ids.yaml create mode 100644 tests/cassettes/TestValidateDidWebIntegration.test_nonexistent_domain_returns_error.yaml create mode 100644 tests/cassettes/TestValidateDidWebIntegration.test_valid_did_web_resolves.yaml delete mode 100644 tests/test_cli.py diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_create_challenges.py b/ce/iir/api/iir_client/api/iir_api/iir_api_create_challenges.py index 5e45258..b7ae0e4 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_create_challenges.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_create_challenges.py @@ -28,7 +28,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "post", - "url": "/api/iir/createChallenges", + "url": "/iir-api/createChallenges", } if isinstance(body, CreateChallengesRequest): diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py b/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py index 19091da..817ed73 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_federation_fetch.py @@ -32,7 +32,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/federationFetch", + "url": "/iir-api/federationFetch", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuer_by_did.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuer_by_did.py index 9c16209..fa8a96f 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuer_by_did.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuer_by_did.py @@ -32,7 +32,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/issuerByDid", + "url": "/iir-api/issuerByDid", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py index 8b7940e..b24f024 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers.py @@ -42,7 +42,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/issuers", + "url": "/iir-api/issuers", "params": params, } @@ -101,9 +101,9 @@ def sync_detailed( kwargs = _get_kwargs( page=page, - page_size=page_size, - keyword=keyword, - task=task, +page_size=page_size, +keyword=keyword, +task=task, ) @@ -174,9 +174,9 @@ async def asyncio_detailed( kwargs = _get_kwargs( page=page, - page_size=page_size, - keyword=keyword, - task=task, +page_size=page_size, +keyword=keyword, +task=task, ) @@ -213,9 +213,9 @@ async def asyncio( return (await asyncio_detailed( client=client, - page=page, - page_size=page_size, - keyword=keyword, - task=task, +page=page, +page_size=page_size, +keyword=keyword, +task=task, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py index 9a4f786..8a880e3 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_issuers_by_user.py @@ -44,7 +44,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/users/{user_id}/issuers".format(user_id=quote(str(user_id), safe=""),), + "url": "/iir-api/users/{user_id}/issuers".format(user_id=quote(str(user_id), safe=""),), "params": params, } @@ -105,10 +105,10 @@ def sync_detailed( kwargs = _get_kwargs( user_id=user_id, - page=page, - page_size=page_size, - keyword=keyword, - task=task, +page=page, +page_size=page_size, +keyword=keyword, +task=task, ) @@ -147,11 +147,11 @@ def sync( return sync_detailed( user_id=user_id, - client=client, - page=page, - page_size=page_size, - keyword=keyword, - task=task, +client=client, +page=page, +page_size=page_size, +keyword=keyword, +task=task, ).parsed @@ -226,10 +226,10 @@ async def asyncio( return (await asyncio_detailed( user_id=user_id, - client=client, - page=page, - page_size=page_size, - keyword=keyword, - task=task, +client=client, +page=page, +page_size=page_size, +keyword=keyword, +task=task, )).parsed diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py index c12561b..62ec496 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_organization_iir_detail.py @@ -32,7 +32,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/getOrganizationIIRDetail", + "url": "/iir-api/getOrganizationIIRDetail", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_get_registry_resource.py b/ce/iir/api/iir_client/api/iir_api/iir_api_get_registry_resource.py index 527df04..29615e3 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_get_registry_resource.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_get_registry_resource.py @@ -32,7 +32,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/getRegistryResource", + "url": "/iir-api/getRegistryResource", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py index aa909b9..9bfe7d1 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member.py @@ -37,7 +37,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/isUserAMember/{user_id}".format(user_id=quote(str(user_id), safe=""),), + "url": "/iir-api/isUserAMember/{user_id}".format(user_id=quote(str(user_id), safe=""),), "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py index 3d7c274..c3679b2 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_is_user_a_member_of_org.py @@ -35,7 +35,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/IsUserAMemberOfOrg", + "url": "/iir-api/IsUserAMemberOfOrg", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_save_challenge_token.py b/ce/iir/api/iir_client/api/iir_api/iir_api_save_challenge_token.py index c14bc6f..7542beb 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_save_challenge_token.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_save_challenge_token.py @@ -28,7 +28,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "post", - "url": "/api/iir/saveChallengeToken", + "url": "/iir-api/saveChallengeToken", } if isinstance(body, SaveChallengeTokenRequest): diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py b/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py index dc2e610..a374faf 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_submit_to_iir.py @@ -28,7 +28,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "post", - "url": "/api/iir/submitToIIR", + "url": "/iir-api/submitToIIR", } if isinstance(body, PubIssuerDTO): diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py b/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py index e709c81..0bbd8b9 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_update_issuer.py @@ -28,7 +28,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "put", - "url": "/api/iir/updateIssuer", + "url": "/iir-api/updateIssuer", } if isinstance(body, PubIssuerDTO): diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_jwt_signature.py b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_jwt_signature.py index f611c17..7ab600f 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_jwt_signature.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_jwt_signature.py @@ -28,7 +28,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "post", - "url": "/api/iir/validateJwt", + "url": "/iir-api/validateJwt", } if isinstance(body, ValidateJwtRequest): diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_key.py b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_key.py index e6b219d..b4a1e5b 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_key.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_key.py @@ -35,7 +35,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/validateDidKey", + "url": "/iir-api/validateDidKey", "params": params, } diff --git a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_web.py b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_web.py index b557e16..0573ff1 100644 --- a/ce/iir/api/iir_client/api/iir_api/iir_api_validate_web.py +++ b/ce/iir/api/iir_client/api/iir_api/iir_api_validate_web.py @@ -32,7 +32,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/api/iir/validateDidWeb", + "url": "/iir-api/validateDidWeb", "params": params, } diff --git a/ce/iir/csv_processor.py b/ce/iir/csv_processor.py index 492be3b..a2c2f05 100644 --- a/ce/iir/csv_processor.py +++ b/ce/iir/csv_processor.py @@ -21,6 +21,7 @@ from rich.table import Table, box from ce.iir.did_ops import ( + call_get_organization_iir_detail, call_is_user_member, call_create_challenges, call_get_registry_resource, @@ -29,6 +30,7 @@ call_validate_did_web, call_verify_jwt_signature, classify_did, + merge_autofill, sign_proof_jwt, validate_ctid, validate_date, @@ -335,13 +337,22 @@ def pub_fail(reason: str) -> None: if not ok: pub_fail(err) else: + ok_acc, accounts_data, acc_err = call_get_organization_iir_detail( + ctid, access_token, env.publisher_base, env.ssl_verify + ) + if not ok_acc: + accounts_data = {} + + merged = merge_autofill(registry_data, accounts_data) ok, err = call_submit_to_iir( ctid, did, - registry_data.get("Name", ""), - registry_data.get("LegalName", ""), - registry_data.get("CredentialRegistryUri", ""), - registry_data.get("SubjectWebpage", ""), + merged["name"], + merged["legal_name"], + merged["registry_uri"], + merged["subject_webpage"], access_token, env.publisher_base, env.ssl_verify, + logo_uri=merged["image"], + logo_base64=merged["logo_base64"], valid_from=valid_from, valid_until=valid_until, ) diff --git a/ce/iir/openapi.json b/ce/iir/openapi.json index fb828c3..448411d 100644 --- a/ce/iir/openapi.json +++ b/ce/iir/openapi.json @@ -1,955 +1,985 @@ { - "openapi": "3.0.0", - "info": { - "version": "v1", - "title": "CTI Directory API" - }, - "paths": { - "/api/iir/IsUserAMemberOfOrg": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_IsUserAMemberOfOrg", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "task", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } - } - } + "openapi": "3.0.0", + "info": { + "version": "v1", + "title": "CTI Directory API" }, - "/api/iir/isUserAMember/{userId}": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_IsUserAMember", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "task", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + "paths": { + "/iir-api/IsUserAMemberOfOrg": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMemberOfOrg", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "task", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } - } - } - }, - "/api/iir/getOrganizationIIRDetail": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetOrganizationIIRDetail", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/isUserAMember/{userId}": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMember", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "task", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } - } - } - }, - "/api/iir/getRegistryResource": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetRegistryResource", - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/getOrganizationIIRDetail": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetOrganizationIIRDetail", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } - } - } - } - } - }, - "/api/iir/validateDidWeb": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateWeb", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/getRegistryResource": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetRegistryResource", + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } - } - } - }, - "/api/iir/validateDidKey": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateKey", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "alg", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/validateDidWeb": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateWeb", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } - } - } - } - } - }, - "/api/iir/createChallenges": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_CreateChallenges", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CreateChallengesRequest" - } } - }, - "required": true }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + "/iir-api/validateDidKey": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateKey", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "alg", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } - } - } - }, - "/api/iir/validateJwt": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_ValidateJwtSignature", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ValidateJwtRequest" - } - } - }, - "required": true }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" + "/iir-api/createChallenges": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_CreateChallenges", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CreateChallengesRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/api/iir/saveChallengeToken": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_SaveChallengeToken", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SaveChallengeTokenRequest" - } } - }, - "required": true }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" + "/iir-api/validateJwt": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateJwtSignature", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ValidateJwtRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } } - } - } - } - }, - "/api/iir/submitToIIR": { - "post": { - "tags": ["IIRApi"], - "operationId": "IIRApi_SubmitToIIR", - "requestBody": { - "$ref": "#/components/requestBodies/PubIssuerDTO" }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" + "/iir-api/saveChallengeToken": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SaveChallengeToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SaveChallengeTokenRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/api/iir/issuers": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuers", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "pageSize", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "keyword", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "task", - "in": "query", - "required": false, - "schema": { - "type": "string" } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/submitToIIR": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SubmitToIIR", + "requestBody": { + "$ref": "#/components/requestBodies/PubIssuerDTO" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } - } - } - }, - "/api/iir/users/{userId}/issuers": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuersByUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "pageSize", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "keyword", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "task", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" - } - }, - "application/xml": { - "schema": { - "type": "object" - } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "/iir-api/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuers", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "task", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } - } - } - } - } - }, - "/api/iir/federationFetch": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_FederationFetch", - "parameters": [ - { - "name": "sub", - "in": "query", - "required": true, - "schema": { - "type": "string" } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" + }, + "/iir-api/users/{userId}/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuersByUser", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "task", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "text/json": { - "schema": { - "type": "object" + } + }, + "/iir-api/federationFetch": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_FederationFetch", + "parameters": [ + { + "name": "sub", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "application/xml": { - "schema": { - "type": "object" + } + }, + "/iir-api/issuerByDid": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuerByDid", + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - }, - "text/xml": { - "schema": { - "type": "object" + } + }, + "/iir-api/updateIssuer": { + "put": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_UpdateIssuer", + "requestBody": { + "$ref": "#/components/requestBodies/PubIssuerDTO" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "text/json": { + "schema": { + "type": "object" + } + }, + "application/xml": { + "schema": { + "type": "object" + } + }, + "text/xml": { + "schema": { + "type": "object" + } + } + } + } } - } } - } } - } }, - "/api/iir/issuerByDid": { - "get": { - "tags": ["IIRApi"], - "operationId": "IIRApi_GetIssuerByDid", - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "schema": { - "type": "string" + "servers": [ + { + "url": "https://localhost:44330" + } + ], + "components": { + "requestBodies": { + "PubIssuerDTO": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PubIssuerDTO" + } + } + }, + "required": true } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - }, - "text/json": { - "schema": { - "type": "object" + }, + "schemas": { + "CancellationToken": { + "type": "object", + "properties": { + "IsCancellationRequested": { + "type": "boolean", + "readOnly": true + }, + "CanBeCanceled": { + "type": "boolean", + "readOnly": true + }, + "WaitHandle": { + "$ref": "#/components/schemas/WaitHandle" + } } - }, - "application/xml": { - "schema": { - "type": "object" + }, + "WaitHandle": { + "type": "object", + "properties": { + "Handle": { + "type": "object" + }, + "SafeWaitHandle": { + "$ref": "#/components/schemas/SafeWaitHandle" + } } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "SafeWaitHandle": { + "type": "object", + "properties": { + "IsInvalid": { + "type": "boolean", + "readOnly": true + }, + "IsClosed": { + "type": "boolean", + "readOnly": true + } } - } - } - } - } - } - }, - "/api/iir/updateIssuer": { - "put": { - "tags": ["IIRApi"], - "operationId": "IIRApi_UpdateIssuer", - "requestBody": { - "$ref": "#/components/requestBodies/PubIssuerDTO" - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" + }, + "CreateChallengesRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "VerificationMethodIds": { + "type": "array", + "items": { + "type": "string" + } + } } - }, - "text/json": { - "schema": { - "type": "object" + }, + "ValidateJwtRequest": { + "type": "object", + "properties": { + "Jwt": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } } - }, - "application/xml": { - "schema": { - "type": "object" + }, + "SaveChallengeTokenRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + }, + "Token": { + "type": "string" + } } - }, - "text/xml": { - "schema": { - "type": "object" + }, + "PubIssuerDTO": { + "type": "object", + "properties": { + "CTID": { + "type": "string" + }, + "DID": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "LegalName": { + "type": "string" + }, + "CredentialRegistryUri": { + "type": "string" + }, + "SubjectWebPage": { + "type": "string" + }, + "LogoBase64": { + "type": "string" + }, + "LogoUri": { + "type": "string" + }, + "ValidFrom": { + "format": "date-time", + "type": "string" + }, + "ValidUntil": { + "format": "date-time", + "type": "string" + } } - } } - } - } - } - } - }, - "servers": [ - { - "url": "https://localhost:44330" - } - ], - "components": { - "requestBodies": { - "PubIssuerDTO": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PubIssuerDTO" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/PubIssuerDTO" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/PubIssuerDTO" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/PubIssuerDTO" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PubIssuerDTO" - } - } - }, - "required": true - } - }, - "schemas": { - "CancellationToken": { - "type": "object", - "properties": { - "IsCancellationRequested": { - "type": "boolean", - "readOnly": true - }, - "CanBeCanceled": { - "type": "boolean", - "readOnly": true - }, - "WaitHandle": { - "$ref": "#/components/schemas/WaitHandle" - } - } - }, - "WaitHandle": { - "type": "object", - "properties": { - "Handle": { - "type": "object" - }, - "SafeWaitHandle": { - "$ref": "#/components/schemas/SafeWaitHandle" - } - } - }, - "SafeWaitHandle": { - "type": "object", - "properties": { - "IsInvalid": { - "type": "boolean", - "readOnly": true - }, - "IsClosed": { - "type": "boolean", - "readOnly": true - } - } - }, - "CreateChallengesRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "VerificationMethodIds": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ValidateJwtRequest": { - "type": "object", - "properties": { - "Jwt": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - } - } - }, - "SaveChallengeTokenRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - }, - "Token": { - "type": "string" - } - } - }, - "PubIssuerDTO": { - "type": "object", - "properties": { - "CTID": { - "type": "string" - }, - "DID": { - "type": "string" - }, - "Name": { - "type": "string" - }, - "LegalName": { - "type": "string" - }, - "CredentialRegistryUri": { - "type": "string" - }, - "SubjectWebPage": { - "type": "string" - }, - "LogoBase64": { - "type": "string" - }, - "LogoUri": { - "type": "string" - }, - "ValidFrom": { - "format": "date-time", - "type": "string" - }, - "ValidUntil": { - "format": "date-time", - "type": "string" - } } - } } - } -} +} \ No newline at end of file diff --git a/ce/iir/swagger.json b/ce/iir/swagger.json index 3441ae5..546034d 100644 --- a/ce/iir/swagger.json +++ b/ce/iir/swagger.json @@ -1,713 +1,713 @@ { - "swagger": "2.0", - "info": { - "version": "v1", - "title": "CTI Directory API" - }, - "host": "localhost:44330", - "schemes": [ - "https" - ], - "paths": { - "/api/iir/IsUserAMemberOfOrg": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_IsUserAMemberOfOrg", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "task", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" - } - } - } - } - }, - "/api/iir/isUserAMember/{userId}": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_IsUserAMember", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string", - "format": "uuid" - }, - { - "name": "task", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" - } - } - } - } + "swagger": "2.0", + "info": { + "version": "v1", + "title": "CTI Directory API" }, - "/api/iir/getOrganizationIIRDetail": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetOrganizationIIRDetail", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + "host": "localhost:44330", + "schemes": [ + "https" + ], + "paths": { + "/iir-api/IsUserAMemberOfOrg": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMemberOfOrg", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/getRegistryResource": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetRegistryResource", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "ctid", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" - } - } - } - } - }, - "/api/iir/validateDidWeb": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateWeb", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/isUserAMember/{userId}": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_IsUserAMember", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "task", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/validateDidKey": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateKey", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - }, - { - "name": "alg", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/getOrganizationIIRDetail": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetOrganizationIIRDetail", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/createChallenges": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_CreateChallenges", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateChallengesRequest" + }, + "/iir-api/getRegistryResource": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetRegistryResource", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "ctid", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/validateDidWeb": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateWeb", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/validateJwt": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_ValidateJwtSignature", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ValidateJwtRequest" + }, + "/iir-api/validateDidKey": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateKey", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + }, + { + "name": "alg", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/createChallenges": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_CreateChallenges", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CreateChallengesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/saveChallengeToken": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_SaveChallengeToken", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "model", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SaveChallengeTokenRequest" + }, + "/iir-api/validateJwt": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_ValidateJwtSignature", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/ValidateJwtRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/saveChallengeToken": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SaveChallengeToken", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "model", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SaveChallengeTokenRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/submitToIIR": { - "post": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_SubmitToIIR", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "dto", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/PubIssuerDTO" + }, + "/iir-api/submitToIIR": { + "post": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_SubmitToIIR", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "dto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/PubIssuerDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuers", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "keyword", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/issuers": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuers", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "pageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "keyword", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "task", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/users/{userId}/issuers": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuersByUser", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "page", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "keyword", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "task", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/users/{userId}/issuers": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuersByUser", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string", - "format": "uuid" - }, - { - "name": "page", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "pageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "keyword", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "task", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/federationFetch": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_FederationFetch", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "sub", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/federationFetch": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_FederationFetch", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "sub", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/issuerByDid": { + "get": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_GetIssuerByDid", + "consumes": [], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } - } - } - }, - "/api/iir/issuerByDid": { - "get": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_GetIssuerByDid", - "consumes": [], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "did", - "in": "query", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" + }, + "/iir-api/updateIssuer": { + "put": { + "tags": [ + "IIRApi" + ], + "operationId": "IIRApi_UpdateIssuer", + "consumes": [ + "application/json", + "text/json", + "application/xml", + "text/xml", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "text/json", + "application/xml", + "text/xml" + ], + "parameters": [ + { + "name": "dto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/PubIssuerDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } } - } } - } }, - "/api/iir/updateIssuer": { - "put": { - "tags": [ - "IIRApi" - ], - "operationId": "IIRApi_UpdateIssuer", - "consumes": [ - "application/json", - "text/json", - "application/xml", - "text/xml", - "application/x-www-form-urlencoded" - ], - "produces": [ - "application/json", - "text/json", - "application/xml", - "text/xml" - ], - "parameters": [ - { - "name": "dto", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/PubIssuerDTO" + "definitions": { + "CancellationToken": { + "type": "object", + "properties": { + "IsCancellationRequested": { + "type": "boolean", + "readOnly": true + }, + "CanBeCanceled": { + "type": "boolean", + "readOnly": true + }, + "WaitHandle": { + "$ref": "#/definitions/WaitHandle", + "readOnly": true + } } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object" - } - } - } - } - } - }, - "definitions": { - "CancellationToken": { - "type": "object", - "properties": { - "IsCancellationRequested": { - "type": "boolean", - "readOnly": true - }, - "CanBeCanceled": { - "type": "boolean", - "readOnly": true }, "WaitHandle": { - "$ref": "#/definitions/WaitHandle", - "readOnly": true - } - } - }, - "WaitHandle": { - "type": "object", - "properties": { - "Handle": { - "type": "object" + "type": "object", + "properties": { + "Handle": { + "type": "object" + }, + "SafeWaitHandle": { + "$ref": "#/definitions/SafeWaitHandle" + } + } }, "SafeWaitHandle": { - "$ref": "#/definitions/SafeWaitHandle" - } - } - }, - "SafeWaitHandle": { - "type": "object", - "properties": { - "IsInvalid": { - "type": "boolean", - "readOnly": true - }, - "IsClosed": { - "type": "boolean", - "readOnly": true - } - } - }, - "CreateChallengesRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "VerificationMethodIds": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ValidateJwtRequest": { - "type": "object", - "properties": { - "Jwt": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - } - } - }, - "SaveChallengeTokenRequest": { - "type": "object", - "properties": { - "Ctid": { - "type": "string" - }, - "ChallengeId": { - "format": "uuid", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - }, - "Token": { - "type": "string" - } - } - }, - "PubIssuerDTO": { - "type": "object", - "properties": { - "CTID": { - "type": "string" - }, - "DID": { - "type": "string" - }, - "Name": { - "type": "string" - }, - "LegalName": { - "type": "string" - }, - "CredentialRegistryUri": { - "type": "string" - }, - "SubjectWebPage": { - "type": "string" + "type": "object", + "properties": { + "IsInvalid": { + "type": "boolean", + "readOnly": true + }, + "IsClosed": { + "type": "boolean", + "readOnly": true + } + } }, - "LogoBase64": { - "type": "string" + "CreateChallengesRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "VerificationMethodIds": { + "type": "array", + "items": { + "type": "string" + } + } + } }, - "LogoUri": { - "type": "string" + "ValidateJwtRequest": { + "type": "object", + "properties": { + "Jwt": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } + } }, - "ValidFrom": { - "format": "date-time", - "type": "string" + "SaveChallengeTokenRequest": { + "type": "object", + "properties": { + "Ctid": { + "type": "string" + }, + "ChallengeId": { + "format": "uuid", + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + }, + "Token": { + "type": "string" + } + } }, - "ValidUntil": { - "format": "date-time", - "type": "string" + "PubIssuerDTO": { + "type": "object", + "properties": { + "CTID": { + "type": "string" + }, + "DID": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "LegalName": { + "type": "string" + }, + "CredentialRegistryUri": { + "type": "string" + }, + "SubjectWebPage": { + "type": "string" + }, + "LogoBase64": { + "type": "string" + }, + "LogoUri": { + "type": "string" + }, + "ValidFrom": { + "format": "date-time", + "type": "string" + }, + "ValidUntil": { + "format": "date-time", + "type": "string" + } + } } - } } - } } \ No newline at end of file diff --git a/tests/cassettes/TestFullChallengeFlowIntegration.test_create_challenge.yaml b/tests/cassettes/TestFullChallengeFlowIntegration.test_create_challenge.yaml new file mode 100644 index 0000000..839b4ae --- /dev/null +++ b/tests/cassettes/TestFullChallengeFlowIntegration.test_create_challenge.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: Ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&VerificationMethodIds=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic%23z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '178' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/createChallenges + response: + body: + string: '[{"Challenge":"0b006f04-40c3-4641-808e-6de966cc6651","Did":"did:key:z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic#z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic","Aud":"https://issuerregistry.credentialengine.org","Iat":1778189668,"Exp":1778276068,"Ctid":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","CreatedDate":"2026-05-07T21:34:28.7799348+00:00"}]' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '353' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:27 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcY3JlYXRlQ2hhbGxlbmdlcw==?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestFullChallengeFlowIntegration.test_full_sign_verify_save_flow.yaml b/tests/cassettes/TestFullChallengeFlowIntegration.test_full_sign_verify_save_flow.yaml new file mode 100644 index 0000000..9ffbca0 --- /dev/null +++ b/tests/cassettes/TestFullChallengeFlowIntegration.test_full_sign_verify_save_flow.yaml @@ -0,0 +1,134 @@ +interactions: +- request: + body: Ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&VerificationMethodIds=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic%23z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '178' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/createChallenges + response: + body: + string: '[{"Challenge":"a7abef34-7b8a-4b98-98ff-8c8c1be8b7d9","Did":"did:key:z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic#z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic","Aud":"https://issuerregistry.credentialengine.org","Iat":1778189668,"Exp":1778276068,"Ctid":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","CreatedDate":"2026-05-07T21:34:28.9188195+00:00"}]' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '353' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:28 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcY3JlYXRlQ2hhbGxlbmdlcw==?= + status: + code: 200 + message: OK +- request: + body: Jwt=eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2ZVdko5UUo3R2g2eTlNWFV5QnlMYzNieGh1UDl5ZmZIOGlIU05CbWhoQmljI3o2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyIsInR5cCI6IkpXVCJ9.eyJkaWQiOiJkaWQ6a2V5Ono2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyN6Nk1rZlV2SjlRSjdHaDZ5OU1YVXlCeUxjM2J4aHVQOXlmZkg4aUhTTkJtaGhCaWMiLCJjaGFsbGVuZ2UiOiJhN2FiZWYzNC03YjhhLTRiOTgtOThmZi04YzhjMWJlOGI3ZDkiLCJhdWQiOiJodHRwczovL2lzc3VlcnJlZ2lzdHJ5LmNyZWRlbnRpYWxlbmdpbmUub3JnIiwiaWF0IjoxNzc4MTg5NjY4LCJleHAiOjE3NzgyNzYwNjgsImN0aWQiOiJjZS1hNDA0MTk4My1iMWFlLTRhZDQtYTQzZC0yODRhNWI0YjJkNzMifQ.sffkQ2qrMSLJlIc0K-jUIJ71l-uu1TETag8vwUZpUuBd9fEbZP_QrTkkzu485zJpJy3kRgkcVBaAk1D3_yaLCQ&ChallengeId=a7abef34-7b8a-4b98-98ff-8c8c1be8b7d9 + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '731' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/validateJwt + response: + body: + string: '{"Valid":true}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '14' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:28 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVKd3Q=?= + status: + code: 200 + message: OK +- request: + body: Ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&ChallengeId=a7abef34-7b8a-4b98-98ff-8c8c1be8b7d9&Token=eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2ZVdko5UUo3R2g2eTlNWFV5QnlMYzNieGh1UDl5ZmZIOGlIU05CbWhoQmljI3o2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyIsInR5cCI6IkpXVCJ9.eyJkaWQiOiJkaWQ6a2V5Ono2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyN6Nk1rZlV2SjlRSjdHaDZ5OU1YVXlCeUxjM2J4aHVQOXlmZkg4aUhTTkJtaGhCaWMiLCJjaGFsbGVuZ2UiOiJhN2FiZWYzNC03YjhhLTRiOTgtOThmZi04YzhjMWJlOGI3ZDkiLCJhdWQiOiJodHRwczovL2lzc3VlcnJlZ2lzdHJ5LmNyZWRlbnRpYWxlbmdpbmUub3JnIiwiaWF0IjoxNzc4MTg5NjY4LCJleHAiOjE3NzgyNzYwNjgsImN0aWQiOiJjZS1hNDA0MTk4My1iMWFlLTRhZDQtYTQzZC0yODRhNWI0YjJkNzMifQ.sffkQ2qrMSLJlIc0K-jUIJ71l-uu1TETag8vwUZpUuBd9fEbZP_QrTkkzu485zJpJy3kRgkcVBaAk1D3_yaLCQ + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '778' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/saveChallengeToken + response: + body: + string: '{"ChallengeId":"a7abef34-7b8a-4b98-98ff-8c8c1be8b7d9","Ctid":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","Token":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2ZVdko5UUo3R2g2eTlNWFV5QnlMYzNieGh1UDl5ZmZIOGlIU05CbWhoQmljI3o2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyIsInR5cCI6IkpXVCJ9.eyJkaWQiOiJkaWQ6a2V5Ono2TWtmVXZKOVFKN0doNnk5TVhVeUJ5TGMzYnhodVA5eWZmSDhpSFNOQm1oaEJpYyN6Nk1rZlV2SjlRSjdHaDZ5OU1YVXlCeUxjM2J4aHVQOXlmZkg4aUhTTkJtaGhCaWMiLCJjaGFsbGVuZ2UiOiJhN2FiZWYzNC03YjhhLTRiOTgtOThmZi04YzhjMWJlOGI3ZDkiLCJhdWQiOiJodHRwczovL2lzc3VlcnJlZ2lzdHJ5LmNyZWRlbnRpYWxlbmdpbmUub3JnIiwiaWF0IjoxNzc4MTg5NjY4LCJleHAiOjE3NzgyNzYwNjgsImN0aWQiOiJjZS1hNDA0MTk4My1iMWFlLTRhZDQtYTQzZC0yODRhNWI0YjJkNzMifQ.sffkQ2qrMSLJlIc0K-jUIJ71l-uu1TETag8vwUZpUuBd9fEbZP_QrTkkzu485zJpJy3kRgkcVBaAk1D3_yaLCQ","IsTokenVerified":true,"CreatedDate":"2026-05-07T21:34:29.1912694+00:00"}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '865' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:28 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcc2F2ZUNoYWxsZW5nZVRva2Vu?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_known_ctid_returns_org_detail_or_skips.yaml b/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_known_ctid_returns_org_detail_or_skips.yaml new file mode 100644 index 0000000..7c02c57 --- /dev/null +++ b/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_known_ctid_returns_org_detail_or_skips.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getOrganizationIIRDetail?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"valid":true,"data":{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","LegalName":"Credential + Engine","LogoUrl":"https://credentialengine.org/wp-content/themes/credential-engine/img/logo.png"}}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '194' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:26 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0T3JnYW5pemF0aW9uSUlSRGV0YWls?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_unknown_ctid_returns_failure.yaml b/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_unknown_ctid_returns_failure.yaml new file mode 100644 index 0000000..b4e93fc --- /dev/null +++ b/tests/cassettes/TestGetOrganizationIIRDetailIntegration.test_unknown_ctid_returns_failure.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getOrganizationIIRDetail?ctid=ce-00000000-0000-0000-0000-999999999999 + response: + body: + string: '{"Error":"{\"data\":null,\"valid\":false,\"status\":\"Organization + not found.\",\"extra\":null}"}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '97' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:26 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0T3JnYW5pemF0aW9uSUlSRGV0YWls?= + status: + code: 404 + message: Not Found +version: 1 diff --git a/tests/cassettes/TestGetRegistryResourceIntegration.test_known_ctid_exists_in_registry.yaml b/tests/cassettes/TestGetRegistryResourceIntegration.test_known_ctid_exists_in_registry.yaml new file mode 100644 index 0000000..a849d1b --- /dev/null +++ b/tests/cassettes/TestGetRegistryResourceIntegration.test_known_ctid_exists_in_registry.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getRegistryResource?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","ExistsInRegistry":true,"RegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","CTDLType":"ceterms:CredentialOrganization","Name":"Credential + Engine Administration - Sandbox","LegalName":"Credential Engine Administration + - Sandbox","CredentialRegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","SubjectWebpage":"https://sandbox.credentialengine.org/","Image":null}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '529' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:24 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0UmVnaXN0cnlSZXNvdXJjZQ==?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestGetRegistryResourceIntegration.test_unknown_ctid_not_found.yaml b/tests/cassettes/TestGetRegistryResourceIntegration.test_unknown_ctid_not_found.yaml new file mode 100644 index 0000000..93810bd --- /dev/null +++ b/tests/cassettes/TestGetRegistryResourceIntegration.test_unknown_ctid_not_found.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getRegistryResource?ctid=ce-00000000-0000-0000-0000-999999999999 + response: + body: + string: '{"CTID":"ce-00000000-0000-0000-0000-999999999999","ExistsInRegistry":false,"RegistryUri":"","CTDLType":""}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '106' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:26 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0UmVnaXN0cnlSZXNvdXJjZQ==?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestIsUserMemberIntegration.test_bogus_ctid_returns_error.yaml b/tests/cassettes/TestIsUserMemberIntegration.test_bogus_ctid_returns_error.yaml new file mode 100644 index 0000000..e67f60c --- /dev/null +++ b/tests/cassettes/TestIsUserMemberIntegration.test_bogus_ctid_returns_error.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/isUserAMember/bf95cac3-ca3d-4e93-838c-257a4cdb46a6?ctid=ce-00000000-0000-0000-0000-999999999999&task=IIR + response: + body: + string: '{"valid":false}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '15' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:23 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcaXNVc2VyQU1lbWJlclxiZjk1Y2FjMy1jYTNkLTRlOTMtODM4Yy0yNTdhNGNkYjQ2YTY=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestIsUserMemberIntegration.test_member_check_returns_a_bool.yaml b/tests/cassettes/TestIsUserMemberIntegration.test_member_check_returns_a_bool.yaml new file mode 100644 index 0000000..12b42c8 --- /dev/null +++ b/tests/cassettes/TestIsUserMemberIntegration.test_member_check_returns_a_bool.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/isUserAMember/bf95cac3-ca3d-4e93-838c-257a4cdb46a6?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&task=IIR + response: + body: + string: '{"valid":true}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '14' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:23 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcaXNVc2VyQU1lbWJlclxiZjk1Y2FjMy1jYTNkLTRlOTMtODM4Yy0yNTdhNGNkYjQ2YTY=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestIsUserMemberIntegration.test_member_of_known_org.yaml b/tests/cassettes/TestIsUserMemberIntegration.test_member_of_known_org.yaml new file mode 100644 index 0000000..12b42c8 --- /dev/null +++ b/tests/cassettes/TestIsUserMemberIntegration.test_member_of_known_org.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/isUserAMember/bf95cac3-ca3d-4e93-838c-257a4cdb46a6?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&task=IIR + response: + body: + string: '{"valid":true}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '14' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:23 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcaXNVc2VyQU1lbWJlclxiZjk1Y2FjMy1jYTNkLTRlOTMtODM4Yy0yNTdhNGNkYjQ2YTY=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestMergeAutofillEndToEnd.test_accounts_legal_name_overrides_when_present.yaml b/tests/cassettes/TestMergeAutofillEndToEnd.test_accounts_legal_name_overrides_when_present.yaml new file mode 100644 index 0000000..8b19b25 --- /dev/null +++ b/tests/cassettes/TestMergeAutofillEndToEnd.test_accounts_legal_name_overrides_when_present.yaml @@ -0,0 +1,85 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getRegistryResource?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","ExistsInRegistry":true,"RegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","CTDLType":"ceterms:CredentialOrganization","Name":"Credential + Engine Administration - Sandbox","LegalName":"Credential Engine Administration + - Sandbox","CredentialRegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","SubjectWebpage":"https://sandbox.credentialengine.org/","Image":null}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '529' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:31 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0UmVnaXN0cnlSZXNvdXJjZQ==?= + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getOrganizationIIRDetail?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"valid":true,"data":{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","LegalName":"Credential + Engine","LogoUrl":"https://credentialengine.org/wp-content/themes/credential-engine/img/logo.png"}}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '194' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:31 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0T3JnYW5pemF0aW9uSUlSRGV0YWls?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestMergeAutofillEndToEnd.test_registry_data_flows_through_merge.yaml b/tests/cassettes/TestMergeAutofillEndToEnd.test_registry_data_flows_through_merge.yaml new file mode 100644 index 0000000..de4595c --- /dev/null +++ b/tests/cassettes/TestMergeAutofillEndToEnd.test_registry_data_flows_through_merge.yaml @@ -0,0 +1,85 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getRegistryResource?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","ExistsInRegistry":true,"RegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","CTDLType":"ceterms:CredentialOrganization","Name":"Credential + Engine Administration - Sandbox","LegalName":"Credential Engine Administration + - Sandbox","CredentialRegistryUri":"https://sandbox.credentialengineregistry.org/resources/ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","SubjectWebpage":"https://sandbox.credentialengine.org/","Image":null}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '529' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:30 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0UmVnaXN0cnlSZXNvdXJjZQ==?= + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/getOrganizationIIRDetail?ctid=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73 + response: + body: + string: '{"valid":true,"data":{"CTID":"ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73","LegalName":"Credential + Engine","LogoUrl":"https://credentialengine.org/wp-content/themes/credential-engine/img/logo.png"}}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '194' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:30 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcZ2V0T3JnYW5pemF0aW9uSUlSRGV0YWls?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestSubmitToIirIntegration.test_submit_returns_success_or_already_exists.yaml b/tests/cassettes/TestSubmitToIirIntegration.test_submit_returns_success_or_already_exists.yaml new file mode 100644 index 0000000..e36d261 --- /dev/null +++ b/tests/cassettes/TestSubmitToIirIntegration.test_submit_returns_success_or_already_exists.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: CTID=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&DID=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic&Name=Acme+Test&LegalName=Acme+Test+Corp&CredentialRegistryUri=https%3A%2F%2Fregistry.example.com&SubjectWebPage=https%3A%2F%2Facme.example.com + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '252' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/submitToIIR + response: + body: + string: '{"Error":""}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '12' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:28 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcc3VibWl0VG9JSVI=?= + status: + code: 409 + message: Conflict +version: 1 diff --git a/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_logo_uri_is_accepted.yaml b/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_logo_uri_is_accepted.yaml new file mode 100644 index 0000000..29c3f27 --- /dev/null +++ b/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_logo_uri_is_accepted.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: CTID=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&DID=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic&Name=Acme+Test&LegalName=Acme+Test+Corp&CredentialRegistryUri=https%3A%2F%2Fregistry.example.com&SubjectWebPage=https%3A%2F%2Facme.example.com&LogoUri=https%3A%2F%2Facme.example.com%2Flogo.png + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '302' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/submitToIIR + response: + body: + string: '{"Error":""}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '12' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:30 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcc3VibWl0VG9JSVI=?= + status: + code: 409 + message: Conflict +version: 1 diff --git a/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_valid_dates_is_accepted.yaml b/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_valid_dates_is_accepted.yaml new file mode 100644 index 0000000..d774970 --- /dev/null +++ b/tests/cassettes/TestSubmitToIirIntegration.test_submit_with_valid_dates_is_accepted.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: CTID=ce-a4041983-b1ae-4ad4-a43d-284a5b4b2d73&DID=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic&Name=Acme+Test&LegalName=Acme+Test+Corp&CredentialRegistryUri=https%3A%2F%2Fregistry.example.com&SubjectWebPage=https%3A%2F%2Facme.example.com&ValidFrom=2024-01-01T00%3A00%3A00%2B00%3A00&ValidUntil=2025-01-01T00%3A00%3A00%2B00%3A00 + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '341' + Content-Type: + - application/x-www-form-urlencoded + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: POST + uri: https://localhost:44330/iir-api/submitToIIR + response: + body: + string: '{"Error":""}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '12' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:28 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcc3VibWl0VG9JSVI=?= + status: + code: 409 + message: Conflict +version: 1 diff --git a/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_key_returns_verification_method_for_known_did.yaml b/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_key_returns_verification_method_for_known_did.yaml new file mode 100644 index 0000000..f1117f6 --- /dev/null +++ b/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_key_returns_verification_method_for_known_did.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidKey?alg=Ed25519&did=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic + response: + body: + string: '{"Algorithm":"Ed25519","VerificationMethodIds":["did:key:z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic#z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic"],"PublicKeyBytes":"D0iEqVnPUSkCcPSlMi6tTI8sWRVUs7R98R67EB1UVRU="}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '221' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:31 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRLZXk=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_web_returns_verification_method_for_known_did.yaml b/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_web_returns_verification_method_for_known_did.yaml new file mode 100644 index 0000000..2532497 --- /dev/null +++ b/tests/cassettes/TestValidateDidDispatcherIntegration.test_did_web_returns_verification_method_for_known_did.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidWeb?did=did%3Aweb%3Asandbox.credentialengine.org + response: + body: + string: '{"DidDocumentUrl":"https://sandbox.credentialengine.org/.well-known/did.json","VerificationMethodIds":["did:web:sandbox.credentialengine.org#z6MkwYGVFm6oFReLYwoHLobjVV5PBjnaYwB9VvJahn4nAx4f"]}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '192' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:31 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRXZWI=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestValidateDidKeyIntegration.test_malformed_did_key_returns_error.yaml b/tests/cassettes/TestValidateDidKeyIntegration.test_malformed_did_key_returns_error.yaml new file mode 100644 index 0000000..ee751c0 --- /dev/null +++ b/tests/cassettes/TestValidateDidKeyIntegration.test_malformed_did_key_returns_error.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidKey?alg=Ed25519&did=did%3Akey%3Azinvalid + response: + body: + string: '{"Error":""}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '12' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:26 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRLZXk=?= + status: + code: 400 + message: Bad Request +version: 1 diff --git a/tests/cassettes/TestValidateDidKeyIntegration.test_valid_did_key_returns_vm_ids.yaml b/tests/cassettes/TestValidateDidKeyIntegration.test_valid_did_key_returns_vm_ids.yaml new file mode 100644 index 0000000..b9bd9a3 --- /dev/null +++ b/tests/cassettes/TestValidateDidKeyIntegration.test_valid_did_key_returns_vm_ids.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidKey?alg=Ed25519&did=did%3Akey%3Az6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic + response: + body: + string: '{"Algorithm":"Ed25519","VerificationMethodIds":["did:key:z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic#z6MkfUvJ9QJ7Gh6y9MXUyByLc3bxhuP9yffH8iHSNBmhhBic"],"PublicKeyBytes":"D0iEqVnPUSkCcPSlMi6tTI8sWRVUs7R98R67EB1UVRU="}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '221' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:26 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRLZXk=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestValidateDidWebIntegration.test_nonexistent_domain_returns_error.yaml b/tests/cassettes/TestValidateDidWebIntegration.test_nonexistent_domain_returns_error.yaml new file mode 100644 index 0000000..f4c4edf --- /dev/null +++ b/tests/cassettes/TestValidateDidWebIntegration.test_nonexistent_domain_returns_error.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidWeb?did=did%3Aweb%3Athis-domain-does-not-exist-xyz.invalid + response: + body: + string: '{"Error":"{\"type\":\"https://tools.ietf.org/html/rfc9110#section-15.6.3\",\"title\":\"Bad + Gateway\",\"status\":502,\"traceId\":\"00-7b6306b31f4d9bb0daab52e6d7b5e0d5-2452abf3eceb879f-00\"}"}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '190' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:27 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRXZWI=?= + status: + code: 502 + message: Bad Gateway +version: 1 diff --git a/tests/cassettes/TestValidateDidWebIntegration.test_valid_did_web_resolves.yaml b/tests/cassettes/TestValidateDidWebIntegration.test_valid_did_web_resolves.yaml new file mode 100644 index 0000000..4d5a211 --- /dev/null +++ b/tests/cassettes/TestValidateDidWebIntegration.test_valid_did_web_resolves.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - localhost:44330 + User-Agent: + - python-httpx/0.28.1 + authorization: + - REDACTED + method: GET + uri: https://localhost:44330/iir-api/validateDidWeb?did=did%3Aweb%3Asandbox.credentialengine.org + response: + body: + string: '{"DidDocumentUrl":"https://sandbox.credentialengine.org/.well-known/did.json","VerificationMethodIds":["did:web:sandbox.credentialengine.org#z6MkwYGVFm6oFReLYwoHLobjVV5PBjnaYwB9VvJahn4nAx4f"]}' + headers: + Access-Control-Expose-Headers: + - Request-Context + Content-Length: + - '192' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 07 May 2026 21:34:27 GMT + Request-Context: + - appId=cid-v1:3de5a21a-3b87-4008-b83b-c667a37cd396 + Server: + - Microsoft-IIS/10.0 + X-Powered-By: + - ASP.NET + X-SourceFiles: + - =?UTF-8?B?RDpcQ3JlZGVudGlhbEVuZ2luZVxQdWJsaXNoZXJcRGlyZWN0b3J5XGlpci1hcGlcdmFsaWRhdGVEaWRXZWI=?= + status: + code: 200 + message: OK +version: 1 diff --git a/tests/conftest.py b/tests/conftest.py index 94579f3..e3e3455 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,17 +2,15 @@ from __future__ import annotations -from unittest.mock import MagicMock +import os from pathlib import Path -from dotenv import load_dotenv - +from unittest.mock import MagicMock import pytest +from dotenv import load_dotenv load_dotenv(Path(__file__).resolve().parents[1] / ".env.test") -import os - PUBLISHER_BASE = os.getenv("CE_PUBLISHER_BASE", "https://localhost:44330") ACCESS_TOKEN = os.getenv("CE_TEST_ACCESS_TOKEN", "") USER_ID = os.getenv("CE_TEST_USER_ID", "") @@ -38,6 +36,10 @@ def pytest_configure(config): "markers", "integration: mark test as an integration test requiring a live server", ) + config.addinivalue_line( + "markers", + "vcr: mark test as using recorded HTTP interactions (pytest-recording)", + ) @pytest.fixture @@ -46,3 +48,32 @@ def mock_env(): env.publisher_base = PUBLISHER_BASE env.ssl_verify = False return env + + + + +@pytest.fixture(scope="session") +def vcr_config(): + return { + "filter_headers": [ + ("authorization", "REDACTED"), + ("cookie", "REDACTED"), + ("x-api-key", "REDACTED"), + ], + "filter_query_parameters": [ + ("access_token", "REDACTED"), + ("token", "REDACTED"), + ], + "filter_post_data_parameters": [ + ("access_token", "REDACTED"), + ("client_secret", "REDACTED"), + ], + "match_on": ["method", "scheme", "host", "port", "path", "query"], + "record_mode": os.getenv("VCR_RECORD_MODE", "none"), + } + + +@pytest.fixture(scope="module") +def vcr_cassette_dir(request): + """Store cassettes alongside the test module under a 'cassettes/' folder.""" + return str(Path(request.module.__file__).parent / "cassettes") \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py deleted file mode 100644 index ff5c880..0000000 --- a/tests/test_cli.py +++ /dev/null @@ -1,547 +0,0 @@ -""" -Unit tests for ce.iir.did_ops. -Run with: - pytest -m "not integration" -s -""" - -from __future__ import annotations -import os -import base64 -import json -import sys -from types import SimpleNamespace -from unittest.mock import MagicMock, patch - -import pytest - - -pytestmark = pytest.mark.unit - -import ce.iir.did_ops as did_ops -from ce.iir.did_ops import ( - _client, - _did_web_to_url, - _extract_ed25519_pub_from_did_key, - _parse_uvarint, - call_create_challenges, - call_get_registry_resource, - call_is_user_member, - call_save_challenge_token, - call_submit_to_iir, - call_validate_did_key, - call_validate_did_web, - call_verify_jwt_signature, - classify_did, - extract_user_id_from_token, - sign_proof_jwt, - validate_ctid, - validate_date, - validate_did_key_prefix, - validate_verification_method, - verify_proof_jwt, -) - - -PUBLISHER_BASE = os.getenv("CE_PUBLISHER_BASE") -ACCESS_TOKEN = os.getenv("CE_TEST_ACCESS_TOKEN") -USER_ID = os.getenv("CE_TEST_USER_ID") -CTID = os.getenv("CE_TEST_CTID") -CHALLENGE_UUID = os.getenv("CE_TEST_CHALLENGE_UUID") -ED25519_DID_KEY = os.getenv("CE_TEST_DID_KEY") -ED25519_KID = ( - f"{ED25519_DID_KEY}#{ED25519_DID_KEY[len('did:key:'):]}" - if ED25519_DID_KEY.startswith("did:key:") - else "" -) - - -def _fake_response(status_code: int, parsed=None) -> SimpleNamespace: - return SimpleNamespace(status_code=status_code, parsed=parsed) - - -class _ParsedObj: - """Simulates the SDK model objects returned by the generated API client. - - The implementation calls `getattr(parsed, "additional_properties", {})` on - non-dict parsed values, so this helper must expose that property. - """ - - def __init__(self, **kwargs): - self._data = kwargs - - @property - def additional_properties(self): - return self._data - - def to_dict(self): - return dict(self._data) - - -class TestClient: - def test_client_strips_trailing_slash(self): - client = _client(ACCESS_TOKEN, PUBLISHER_BASE + "/", ssl_verify=False) - assert client._base_url == PUBLISHER_BASE - assert client.token == ACCESS_TOKEN - assert client._verify_ssl is False - - -class TestValidateCtid: - def test_valid_ctid_passes(self): - ok, err = validate_ctid(CTID) - assert ok - assert err == "" - - def test_empty_string_fails(self): - ok, err = validate_ctid("") - assert not ok - assert "empty" in err.lower() - - def test_wrong_format_fails(self): - ok, err = validate_ctid("not-a-ctid") - assert not ok - assert "ce-guid" in err - - def test_ctid_is_case_insensitive(self): - ok, _ = validate_ctid(CTID.upper()) - assert ok - - -class TestValidateDate: - def test_empty_value_is_treated_as_optional(self): - ok, err, iso = validate_date("", "ValidFrom") - assert ok - assert err == "" and iso == "" - - def test_valid_date_converts_to_iso8601(self): - ok, err, iso = validate_date("01/15/2024", "ValidFrom") - assert ok - assert err == "" - assert iso == "2024-01-15T00:00:00Z" - - def test_iso_format_input_is_rejected(self): - ok, err, _ = validate_date("2024-01-15", "ValidFrom") - assert not ok - assert "MM/DD/YYYY" in err - - def test_impossible_date_fails(self): - ok, err, _ = validate_date("13/99/2024", "ValidFrom") - assert not ok - assert "MM/DD/YYYY" in err - - -class TestClassifyDid: - def test_did_key_recognised(self): - kind, err = classify_did("did:key:z6Mk...") - assert kind == "did:key" and err == "" - - def test_did_web_recognised(self): - kind, err = classify_did("did:web:example.com") - assert kind == "did:web" and err == "" - - def test_unsupported_method_returns_unknown(self): - kind, err = classify_did("did:ethr:0x123") - assert kind == "unknown" - assert "unrecognised" in err - - def test_empty_did_returns_unknown(self): - kind, err = classify_did("") - assert kind == "unknown" - assert "empty" in err.lower() - - -class TestValidateDidKeyPrefix: - def test_ed25519_prefix_matches_algorithm(self): - ok, _ = validate_did_key_prefix("did:key:z6MkhaXgBZ", "Ed25519") - assert ok - - def test_prefix_algorithm_mismatch_fails(self): - ok, err = validate_did_key_prefix("did:key:z6MkhaXgBZ", "P-256") - assert not ok - assert "mismatch" in err - - def test_unsupported_algorithm_fails(self): - ok, err = validate_did_key_prefix("did:key:z6MkhaXgBZ", "RSA") - assert not ok - assert "unsupported" in err - - def test_missing_algorithm_fails(self): - ok, err = validate_did_key_prefix("did:key:z6MkhaXgBZ", "") - assert not ok - assert "required" in err - - def test_unrecognised_key_prefix_fails(self): - ok, err = validate_did_key_prefix("did:key:zXXXunknown", "Ed25519") - assert not ok - assert "unrecognised" in err - - -class TestValidateVerificationMethod: - def test_valid_vm_in_api_list_passes(self): - vm = f"{ED25519_DID_KEY}#key-1" - ok, _ = validate_verification_method(vm, ED25519_DID_KEY, [vm]) - assert ok - - def test_empty_vm_fails(self): - ok, err = validate_verification_method("", ED25519_DID_KEY, []) - assert not ok - assert "empty" in err.lower() - - def test_vm_missing_from_api_list_fails(self): - ok, err = validate_verification_method( - f"{ED25519_DID_KEY}#key-1", ED25519_DID_KEY, [f"{ED25519_DID_KEY}#key-2"] - ) - assert not ok - assert "not found" in err - - def test_vm_belonging_to_wrong_did_fails(self): - ok, err = validate_verification_method("did:key:zOTHER#key-1", ED25519_DID_KEY, []) - assert not ok - assert "does not belong" in err - - def test_empty_api_list_skips_membership_check(self): - ok, _ = validate_verification_method(f"{ED25519_DID_KEY}#key-1", ED25519_DID_KEY, []) - assert ok - - -class TestCallIsUserMember: - def test_returns_true_when_valid_dict_payload(self): - with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, {"valid": True}) - ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert ok and err == "" - - def test_object_payload_with_valid_true_succeeds(self): - # _ParsedObj.additional_properties returns {"valid": True}, which the - # implementation reads via getattr(parsed, "additional_properties", {}). - with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, _ParsedObj(valid=True)) - ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert ok and err == "" - - def test_object_payload_with_valid_false_fails(self): - # additional_properties returns {"valid": False} -> not a member. - with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, _ParsedObj(valid=False)) - ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok - assert "not a member" in err.lower() - - def test_http_error_yields_not_member_message(self): - # The implementation does not inspect status_code after the sync call; - # a 4xx with parsed=None gives data={}, valid=False, and the "not a - # member" message. There is no branch that embeds the status code in err. - with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: - mock_sync.return_value = _fake_response(403, None) - ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok - assert err != "" - - def test_handles_exception_gracefully(self): - with patch("ce.iir.did_ops.is_user_member_sync", side_effect=RuntimeError("timeout")): - ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok and "timeout" in err - - -class TestCallGetRegistryResource: - def test_returns_data_when_ctid_exists(self): - body = _ParsedObj(ExistsInRegistry=True, Name="Acme", LegalName="Acme Corp") - with patch("ce.iir.did_ops.get_registry_resource_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, body) - ok, data, err = call_get_registry_resource(CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert ok and err == "" - assert data["Name"] == "Acme" - - def test_returns_false_when_ctid_not_in_registry_dict_payload(self): - # parsed is a plain dict -> isinstance(parsed, dict) branch sets data = parsed. - # The function returns that dict as-is, not {}. - with patch("ce.iir.did_ops.get_registry_resource_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, {"ExistsInRegistry": False}) - ok, data, err = call_get_registry_resource(CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok - assert data == {"ExistsInRegistry": False} - assert "not found" in err - - def test_400_response_falls_through_to_not_found_message(self): - # There is no explicit 400 branch in call_get_registry_resource. - # parsed=None -> data={} -> ExistsInRegistry missing -> exists=False -> - # returns (False, {}, "CTID '...' not found in registry"). - with patch("ce.iir.did_ops.get_registry_resource_sync") as mock_sync: - mock_sync.return_value = _fake_response(400, None) - ok, data, err = call_get_registry_resource(CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok and data == {} - assert "not found" in err - - def test_exception_returns_api_request_failed_message(self): - with patch( - "ce.iir.did_ops.get_registry_resource_sync", side_effect=RuntimeError("conn refused") - ): - ok, data, err = call_get_registry_resource(CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok and data == {} - assert "conn refused" in err - - -class TestCallValidateDidKey: - def test_returns_verification_method_ids_on_success(self): - body = _ParsedObj(verificationMethodIds=[ED25519_KID]) - with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, body) - ok, data, err = call_validate_did_key( - ED25519_DID_KEY, "Ed25519", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert ok and err == "" - assert ED25519_KID in data["verificationMethodIds"] - - def test_400_returns_generic_failure_message(self): - with patch("ce.iir.did_ops.validate_did_key_sync") as mock_sync: - mock_sync.return_value = _fake_response(400, None) - ok, data, err = call_validate_did_key( - "did:key:bad", "Ed25519", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and data == {} - assert err == "DID validation failed" - - -class TestCallValidateDidWeb: - def test_success(self): - body = _ParsedObj(verificationMethodIds=["did:web:example.com#key-1"]) - with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, body) - ok, data, err = call_validate_did_web( - "did:web:example.com", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert ok and err == "" - assert data["verificationMethodIds"] == ["did:web:example.com#key-1"] - - def test_502_means_remote_did_document_unreachable(self): - with patch("ce.iir.did_ops.validate_did_web_sync") as mock_sync: - mock_sync.return_value = _fake_response(502, None) - ok, data, err = call_validate_did_web( - "did:web:example.com", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and data == {} - assert "502" in err - - -class TestCallCreateChallenges: - def _challenge_body(self): - return _ParsedObj( - Challenge=CHALLENGE_UUID, - Did=ED25519_DID_KEY, - Aud="ce-api", - Iat=1700000000, - Exp=1700003600, - ) - - def test_returns_list_on_success(self): - with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, [self._challenge_body()]) - ok, data, err = call_create_challenges( - CTID, [ED25519_KID], ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert ok and err == "" - assert isinstance(data, list) - assert len(data) == 1 - assert data[0]["Challenge"] == CHALLENGE_UUID - - def test_empty_response_list_is_an_error(self): - with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, []) - ok, data, err = call_create_challenges( - CTID, [ED25519_KID], ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and data == [] - assert "empty" in err - - def test_400_returns_generic_failure_message(self): - with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: - mock_sync.return_value = _fake_response(400, None) - ok, data, err = call_create_challenges( - CTID, ["bad-vm"], ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and data == [] - assert err == "createChallenges failed" - -class TestCallVerifyJwtSignature: - def test_success(self): - with patch("ce.iir.did_ops.validate_jwt_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, None) - ok, err = call_verify_jwt_signature( - "proof.jwt.token", CHALLENGE_UUID, ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert ok - assert err == "" - - def test_404_means_challenge_expired_or_missing(self): - with patch("ce.iir.did_ops.validate_jwt_sync") as mock_sync: - mock_sync.return_value = _fake_response(404, None) - ok, err = call_verify_jwt_signature( - "proof.jwt.token", CHALLENGE_UUID, ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and err == "Challenge not found" - - def test_400_returns_generic_failure_message(self): - with patch("ce.iir.did_ops.validate_jwt_sync") as mock_sync: - mock_sync.return_value = _fake_response(400, None) - ok, err = call_verify_jwt_signature( - "bad.jwt", CHALLENGE_UUID, ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and err == "JWT validation failed" - - -class TestCallSaveChallengeToken: - def test_success(self): - with patch("ce.iir.did_ops.save_challenge_token_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, None) - ok, err = call_save_challenge_token( - CTID, CHALLENGE_UUID, "proof.jwt", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert ok and err == "" - - def test_400_returns_generic_failure_message(self): - with patch("ce.iir.did_ops.save_challenge_token_sync") as mock_sync: - mock_sync.return_value = _fake_response(400, None) - ok, err = call_save_challenge_token( - CTID, CHALLENGE_UUID, "bad.jwt", ACCESS_TOKEN, PUBLISHER_BASE, False - ) - assert not ok and err == "saveChallengeToken failed" - - -class TestCallSubmitToIir: - def _base_args(self): - return ( - CTID, ED25519_DID_KEY, "Acme", "Acme Corp", - "https://registry.example.com", "https://acme.example.com", - ACCESS_TOKEN, PUBLISHER_BASE, False, - ) - - def test_success(self): - with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, None) - ok, err = call_submit_to_iir(*self._base_args()) - assert ok and err == "" - - def test_409_means_did_already_registered(self): - with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: - mock_sync.return_value = _fake_response(409, None) - ok, err = call_submit_to_iir(*self._base_args()) - assert not ok and err == "Did already exists" - - def test_valid_from_and_until_are_passed_as_model_fields(self): - from datetime import datetime, timezone - with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, None) - call_submit_to_iir( - *self._base_args(), - valid_from="2024-01-01T00:00:00Z", - valid_until="2025-01-01T00:00:00Z", - ) - _, kwargs = mock_sync.call_args - body = kwargs["body"] - assert isinstance(body.valid_from, datetime) - assert isinstance(body.valid_until, datetime) - assert body.valid_from == datetime(2024, 1, 1, tzinfo=timezone.utc) - assert body.valid_until == datetime(2025, 1, 1, tzinfo=timezone.utc) - - def test_empty_optional_fields_are_unset_not_none(self): - from ce.iir.api.iir_client.types import UNSET - with patch("ce.iir.did_ops.submit_to_iir_sync") as mock_sync: - mock_sync.return_value = _fake_response(200, None) - call_submit_to_iir(*self._base_args()) - _, kwargs = mock_sync.call_args - body = kwargs["body"] - assert body.logo_uri is UNSET - assert body.logo_base_64 is UNSET - assert body.valid_from is UNSET - assert body.valid_until is UNSET - - -class TestHelpers: - def test_parse_uvarint_roundtrip_example(self): - value, used = _parse_uvarint(bytes([0xED, 0x01])) - assert value == 0xED - assert used == 2 - - def test_extract_ed25519_pub_from_did_key_returns_32_bytes(self): - pub = _extract_ed25519_pub_from_did_key(ED25519_DID_KEY) - assert isinstance(pub, bytes) - assert len(pub) == 32 - - def test_did_web_to_url_without_path(self): - assert _did_web_to_url("did:web:example.com") == "https://example.com/.well-known/did.json" - - def test_did_web_to_url_with_path(self): - assert _did_web_to_url("did:web:example.com:users:alice") == "https://example.com/users/alice/did.json" - - -@pytest.fixture(scope="session") -def private_key() -> str: - key = os.getenv("CE_TEST_PRIVKEY_MULTIBASE", "").strip() - - if not key: - pytest.skip("CE_TEST_PRIVKEY_MULTIBASE not set in .env.test") - - try: - did_ops._extract_ed25519_seed(key) - except Exception as e: - pytest.skip(f"Invalid CE_TEST_PRIVKEY_MULTIBASE format: {e}") - - return key - - -class TestJwtCrypto: - def _make_payload(self) -> dict: - return { - "did": ED25519_DID_KEY, - "challenge": CHALLENGE_UUID, - "aud": "ce-api", - "iat": 1700000000, - "exp": 1700003600, - "ctid": CTID, - } - - def test_sign_produces_a_three_part_jwt(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) - assert isinstance(token, str) - assert token.count(".") == 2 - - def test_sign_and_verify_roundtrip(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) - ok, err = verify_proof_jwt(token, ED25519_KID) - assert ok, err - - def test_tampered_signature_fails_verification(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) - parts = token.split(".") - first = parts[2][0] - parts[2] = ("B" if first != "B" else "C") + parts[2][1:] - ok, err = verify_proof_jwt(".".join(parts), ED25519_KID) - assert not ok and "signature" in err.lower() - - def test_bad_private_key_raises(self): - with pytest.raises(Exception): - sign_proof_jwt("zNOTVALID", ED25519_KID, self._make_payload()) - - -class TestExtractUserIdFromToken: - def _make_token(self, payload: dict) -> str: - header = base64.urlsafe_b64encode(b'{"alg":"RS256"}').rstrip(b"=").decode() - body = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b"=").decode() - return f"{header}.{body}.fakesig" - - def test_extracts_uuid_sub_claim(self): - token = self._make_token({"sub": USER_ID}) - assert extract_user_id_from_token(token) == USER_ID - - def test_missing_sub_raises_value_error(self): - token = self._make_token({"iss": "keycloak"}) - with pytest.raises(ValueError, match="'sub' claim missing"): - extract_user_id_from_token(token) - - def test_non_uuid_sub_raises_value_error(self): - token = self._make_token({"sub": "not-a-uuid"}) - with pytest.raises(ValueError, match="not a valid GUID"): - extract_user_id_from_token(token) - - def test_malformed_token_raises(self): - with pytest.raises(ValueError): - extract_user_id_from_token("not.a.jwt.at.all") \ No newline at end of file diff --git a/tests/test_did_ops_integration.py b/tests/test_did_ops_integration.py index 1cbaf4c..6f45241 100644 --- a/tests/test_did_ops_integration.py +++ b/tests/test_did_ops_integration.py @@ -1,15 +1,17 @@ """ Integration tests for ce.iir.did_ops. -Configuration is loaded from .env.test (never hard-coded here). -Load the file before running, e.g.: +Config comes from .env.test — nothing is hard-coded in this file. Load it +before running, e.g. `set -a && source .env.test && set +a`, or rely on +python-dotenv (it's loaded below if available). - export $(grep -v '^#' .env.test | xargs) && pytest -m integration -s +Two ways to run: -Or with pytest-dotenv / python-dotenv auto-loading if configured in pytest.ini: + # Live: hits the real publisher. Use this to (re-)record cassettes. + VCR_RECORD_MODE=once pytest -m integration -s + # Playback (default): replays cassettes, no network, no secrets needed. pytest -m integration -s - """ from __future__ import annotations @@ -19,56 +21,63 @@ import pytest import requests as req +# Best-effort .env.test loading. If python-dotenv isn't installed we just +# fall back to whatever's already in os.environ. +try: + from dotenv import load_dotenv + + load_dotenv(".env.test") +except ImportError: + pass + from ce.iir.did_ops import ( call_create_challenges, call_get_organization_iir_detail, call_get_registry_resource, call_is_user_member, call_save_challenge_token, + call_submit_to_iir, + call_validate_did, call_validate_did_key, call_validate_did_web, call_verify_jwt_signature, extract_user_id_from_token, + merge_autofill, sign_proof_jwt, verify_proof_jwt, ) -pytestmark = pytest.mark.integration +pytestmark = [pytest.mark.integration, pytest.mark.vcr] # --------------------------------------------------------------------------- -# Config — read once from the environment (populated from .env.test) +# Environment / config # --------------------------------------------------------------------------- +VCR_RECORD_MODE = os.environ.get("VCR_RECORD_MODE", "none").lower() +PLAYBACK_ONLY = VCR_RECORD_MODE == "none" + + PUBLISHER_BASE = os.environ.get("CE_PUBLISHER_BASE", "").rstrip("/") +TEST_CTID = os.environ.get("CE_TEST_CTID", "") +TEST_DID_KEY = os.environ.get("CE_TEST_DID_KEY", "") +TEST_DID_WEB = os.environ.get("CE_TEST_DID_WEB", "") +TEST_VM = os.environ.get("CE_TEST_VM", f"{TEST_DID_KEY}#{TEST_DID_KEY.split(':')[-1]}") +TEST_ALG = os.environ.get("CE_TEST_ALG", "Ed25519") +TEST_USER_ID = os.environ.get("CE_TEST_USER_ID", "").strip() -_RAW_DID_KEY = os.environ.get("CE_TEST_DID_KEY", "") -TEST_CTID = os.environ.get("CE_TEST_CTID", "") -TEST_USER_ID = os.environ.get("CE_TEST_USER_ID", "") -TEST_DID_KEY = _RAW_DID_KEY -TEST_DID_WEB = os.environ.get("CE_TEST_DID_WEB") -TEST_ALG = os.environ.get("CE_TEST_ALG", "Ed25519") -TEST_VM = ( - f"{_RAW_DID_KEY}#{_RAW_DID_KEY[len('did:key:'):]}" - if _RAW_DID_KEY.startswith("did:key:") - else os.environ.get("CE_TEST_VM", "") -) # --------------------------------------------------------------------------- -# Session-scoped fixtures +# Fixtures # --------------------------------------------------------------------------- @pytest.fixture(scope="session", autouse=True) def _require_env(): - """Fail fast with a clear message when mandatory variables are missing.""" - missing = [ - name for name in ( - "CE_PUBLISHER_BASE", - "CE_TEST_ACCESS_TOKEN", - "CE_TEST_CTID", - "CE_TEST_DID_KEY", - ) - if not os.environ.get(name, "").strip() - ] + """Bail out early — with a useful message — if live mode is missing config.""" + if PLAYBACK_ONLY: + return + + required = ("CE_PUBLISHER_BASE", "CE_TEST_ACCESS_TOKEN", "CE_TEST_CTID", "CE_TEST_DID_KEY") + missing = [name for name in required if not os.environ.get(name, "").strip()] if missing: pytest.skip( "Missing required environment variables (load .env.test first): " @@ -78,7 +87,9 @@ def _require_env(): @pytest.fixture(scope="session", autouse=True) def server_available(): - """Skip everything if the API server isn't reachable.""" + """In live/record mode, skip everything if the publisher isn't up.""" + if PLAYBACK_ONLY: + return try: req.get(f"{PUBLISHER_BASE}/api/iir", timeout=5, verify=False) except (req.exceptions.ConnectionError, req.exceptions.Timeout): @@ -87,6 +98,8 @@ def server_available(): @pytest.fixture(scope="session") def access_token() -> str: + if PLAYBACK_ONLY: + return "PLAYBACK_TOKEN" token = os.environ.get("CE_TEST_ACCESS_TOKEN", "").strip() if not token: pytest.skip("CE_TEST_ACCESS_TOKEN not set — skipping integration tests.") @@ -103,11 +116,20 @@ def private_key() -> str: @pytest.fixture(scope="session") def user_id(access_token) -> str: + # Explicit override wins, then playback dummy, then decode the live token. if TEST_USER_ID: return TEST_USER_ID + if PLAYBACK_ONLY: + return "00000000-0000-0000-0000-000000000000" return extract_user_id_from_token(access_token) +@pytest.fixture(scope="module") +def vcr_cassette_dir(request): + """Cassettes sit next to this file in ./cassettes/.""" + return os.path.join(os.path.dirname(request.module.__file__), "cassettes") + + # --------------------------------------------------------------------------- # Tests # --------------------------------------------------------------------------- @@ -129,8 +151,11 @@ def test_member_of_known_org(self, access_token, user_id): def test_bogus_ctid_returns_error(self, access_token, user_id): ok, err = call_is_user_member( - user_id, "ce-00000000-0000-0000-0000-999999999999", - access_token, PUBLISHER_BASE, ssl_verify=False, + user_id, + "ce-00000000-0000-0000-0000-999999999999", + access_token, + PUBLISHER_BASE, + ssl_verify=False, ) assert not ok @@ -141,19 +166,21 @@ def test_known_ctid_exists_in_registry(self, access_token): TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False ) assert ok, f"Registry lookup failed: {err}" + # Either field is fine — different orgs publish different shapes. assert "Name" in data or "LegalName" in data def test_unknown_ctid_not_found(self, access_token): - ok, data, err = call_get_registry_resource( + ok, _data, err = call_get_registry_resource( "ce-00000000-0000-0000-0000-999999999999", - access_token, PUBLISHER_BASE, ssl_verify=False, + access_token, + PUBLISHER_BASE, + ssl_verify=False, ) assert not ok assert err class TestGetOrganizationIIRDetailIntegration: - def test_known_ctid_returns_org_detail_or_skips(self, access_token): ok, data, err = call_get_organization_iir_detail( TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False @@ -166,9 +193,11 @@ def test_known_ctid_returns_org_detail_or_skips(self, access_token): ) def test_unknown_ctid_returns_failure(self, access_token): - ok, _, err = call_get_organization_iir_detail( + ok, _data, err = call_get_organization_iir_detail( "ce-00000000-0000-0000-0000-999999999999", - access_token, PUBLISHER_BASE, ssl_verify=False, + access_token, + PUBLISHER_BASE, + ssl_verify=False, ) assert not ok assert err @@ -183,7 +212,7 @@ def test_valid_did_key_returns_vm_ids(self, access_token): assert data def test_malformed_did_key_returns_error(self, access_token): - ok, data, err = call_validate_did_key( + ok, _data, _err = call_validate_did_key( "did:key:zinvalid", "Ed25519", access_token, PUBLISHER_BASE, ssl_verify=False ) assert not ok @@ -193,15 +222,17 @@ class TestValidateDidWebIntegration: def test_valid_did_web_resolves(self, access_token): if not TEST_DID_WEB: pytest.skip("CE_TEST_DID_WEB not set — skipping did:web integration test.") - ok, data, err = call_validate_did_web( + ok, _data, err = call_validate_did_web( TEST_DID_WEB, access_token, PUBLISHER_BASE, ssl_verify=False ) assert ok, f"DID web validation failed: {err}" def test_nonexistent_domain_returns_error(self, access_token): - ok, data, err = call_validate_did_web( + ok, _data, _err = call_validate_did_web( "did:web:this-domain-does-not-exist-xyz.invalid", - access_token, PUBLISHER_BASE, ssl_verify=False, + access_token, + PUBLISHER_BASE, + ssl_verify=False, ) assert not ok @@ -213,7 +244,9 @@ def test_create_challenge(self, access_token): ) assert ok, f"createChallenges failed: {err}" assert isinstance(challenges, list) and challenges + first = challenges[0] + # The publisher has used both casings over time. assert first.get("Challenge") or first.get("challenge") def test_full_sign_verify_save_flow(self, access_token, private_key): @@ -223,6 +256,7 @@ def test_full_sign_verify_save_flow(self, access_token, private_key): assert ok, f"createChallenges failed: {err}" assert challenges, "createChallenges returned empty list" + # Pick the challenge that matches our VM, otherwise fall back to first. challenge = next( (c for c in challenges if (c.get("Did") or c.get("did")) == TEST_VM), challenges[0], @@ -232,17 +266,18 @@ def test_full_sign_verify_save_flow(self, access_token, private_key): assert challenge_uuid, "Challenge UUID missing from response" payload = { - "did": challenge.get("Did") or challenge.get("did", TEST_DID_KEY), + "did": challenge.get("Did") or challenge.get("did", TEST_DID_KEY), "challenge": challenge_uuid, - "aud": challenge.get("Aud") or challenge.get("aud", ""), - "iat": challenge.get("Iat") or challenge.get("iat", 0), - "exp": challenge.get("Exp") or challenge.get("exp", 0), - "ctid": TEST_CTID, + "aud": challenge.get("Aud") or challenge.get("aud", ""), + "iat": challenge.get("Iat") or challenge.get("iat", 0), + "exp": challenge.get("Exp") or challenge.get("exp", 0), + "ctid": TEST_CTID, } proof_jwt = sign_proof_jwt(private_key, TEST_VM, payload) assert proof_jwt + # Verify locally first — cheap sanity check before hitting the API. ok, err = verify_proof_jwt(proof_jwt, TEST_VM) assert ok, f"Local JWT verification failed: {err}" @@ -252,7 +287,163 @@ def test_full_sign_verify_save_flow(self, access_token, private_key): assert ok, f"API JWT verification failed: {err}" ok, err = call_save_challenge_token( - TEST_CTID, challenge_uuid, proof_jwt, - access_token, PUBLISHER_BASE, ssl_verify=False, + TEST_CTID, + challenge_uuid, + proof_jwt, + access_token, + PUBLISHER_BASE, + ssl_verify=False, + ) + assert ok, f"saveChallengeToken failed: {err}" + + +class TestSubmitToIirIntegration: + def test_submit_returns_success_or_already_exists(self, access_token): + # On a fresh registry the submit succeeds; on a re-run it comes back + # as "Did already exists". Either is fine for this test. + ok, err = call_submit_to_iir( + TEST_CTID, + TEST_DID_KEY, + "Acme Test", + "Acme Test Corp", + "https://registry.example.com", + "https://acme.example.com", + access_token, + PUBLISHER_BASE, + ssl_verify=False, + ) + if not ok: + assert err == "Did already exists", ( + f"Expected success or 'Did already exists', got: {err}" + ) + + def test_submit_with_valid_dates_is_accepted(self, access_token): + ok, err = call_submit_to_iir( + TEST_CTID, + TEST_DID_KEY, + "Acme Test", + "Acme Test Corp", + "https://registry.example.com", + "https://acme.example.com", + access_token, + PUBLISHER_BASE, + ssl_verify=False, + valid_from="2024-01-01T00:00:00Z", + valid_until="2025-01-01T00:00:00Z", + ) + if not ok: + assert err == "Did already exists", ( + f"Expected success or 'Did already exists', got: {err}" + ) + + def test_submit_with_logo_uri_is_accepted(self, access_token): + # Just making sure the optional logo_uri field round-trips. + ok, err = call_submit_to_iir( + TEST_CTID, + TEST_DID_KEY, + "Acme Test", + "Acme Test Corp", + "https://registry.example.com", + "https://acme.example.com", + access_token, + PUBLISHER_BASE, + ssl_verify=False, + logo_uri="https://acme.example.com/logo.png", + ) + if not ok: + assert err == "Did already exists", ( + f"Expected success or 'Did already exists', got: {err}" + ) + + +class TestMergeAutofillEndToEnd: + def test_registry_data_flows_through_merge(self, access_token): + ok_reg, registry_data, err_reg = call_get_registry_resource( + TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False ) - assert ok, f"saveChallengeToken failed: {err}" \ No newline at end of file + assert ok_reg, f"Registry lookup failed: {err_reg}" + + ok_acc, accounts_data, _ = call_get_organization_iir_detail( + TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False + ) + accounts = accounts_data if ok_acc else {} + + merged = merge_autofill(registry_data, accounts) + + expected_keys = { + "name", + "legal_name", + "registry_uri", + "subject_webpage", + "image", + "logo_base64", + } + assert set(merged.keys()) == expected_keys, ( + f"merge_autofill output shape changed: {set(merged.keys())}" + ) + + assert merged["name"], ( + f"Expected non-empty name from registry. " + f"Registry data keys: {list(registry_data.keys())}" + ) + + def test_accounts_legal_name_overrides_when_present(self, access_token): + ok_reg, registry_data, _ = call_get_registry_resource( + TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False + ) + assert ok_reg + + ok_acc, accounts_data, _ = call_get_organization_iir_detail( + TEST_CTID, access_token, PUBLISHER_BASE, ssl_verify=False + ) + if not ok_acc or not accounts_data.get("LegalName"): + pytest.skip("Test org has no Accounts LegalName — can't verify override") + + merged = merge_autofill(registry_data, accounts_data) + assert merged["legal_name"] == accounts_data["LegalName"] + + +class TestValidateDidDispatcherIntegration: + def test_did_key_returns_verification_method_for_known_did(self, access_token): + ok, vm_ids, _data, err = call_validate_did( + TEST_DID_KEY, + TEST_ALG, + access_token, + PUBLISHER_BASE, + ssl_verify=False, + ) + assert ok, f"call_validate_did failed for did:key: {err}" + assert isinstance(vm_ids, list) + assert vm_ids, "Expected at least one verification method ID" + assert any(TEST_DID_KEY in vm for vm in vm_ids), ( + f"Expected '{TEST_DID_KEY}' in verification method IDs: {vm_ids}" + ) + + def test_did_web_returns_verification_method_for_known_did(self, access_token): + if not TEST_DID_WEB: + pytest.skip("CE_TEST_DID_WEB not set — skipping did:web dispatcher test.") + + ok, vm_ids, _data, err = call_validate_did( + TEST_DID_WEB, + "", # alg isn't used for did:web + access_token, + PUBLISHER_BASE, + ssl_verify=False, + ) + assert ok, f"call_validate_did failed for did:web: {err}" + assert isinstance(vm_ids, list) + assert vm_ids, "Expected at least one verification method ID" + + def test_unsupported_did_method_short_circuits_without_api_call(self, access_token): + # Dispatcher should reject unknown methods locally — no point burning + # an API call on something we know we can't handle. + ok, vm_ids, _data, err = call_validate_did( + "did:ethr:0x123", + "", + access_token, + PUBLISHER_BASE, + ssl_verify=False, + ) + assert not ok + assert vm_ids == [] + assert "unrecognised" in err.lower() \ No newline at end of file diff --git a/tests/test_did_ops_unit.py b/tests/test_did_ops_unit.py index bf96099..60eff99 100644 --- a/tests/test_did_ops_unit.py +++ b/tests/test_did_ops_unit.py @@ -1,11 +1,10 @@ """ Unit tests for ce.iir.did_ops. Run with: - pytest -m "not integration" -s + pytest -m unit -s """ from __future__ import annotations -import os import base64 import json from datetime import datetime, timezone @@ -17,7 +16,7 @@ pytestmark = pytest.mark.unit -import ce.iir.did_ops as did_ops + from ce.iir.did_ops import ( IIR_TASK, _client, @@ -46,22 +45,18 @@ validate_verification_method, verify_proof_jwt, ) - from ce.iir.api.iir_client.types import UNSET -PUBLISHER_BASE = os.getenv("CE_PUBLISHER_BASE") -ACCESS_TOKEN = os.getenv("CE_TEST_ACCESS_TOKEN") -USER_ID = os.getenv("CE_TEST_USER_ID") -CTID = os.getenv("CE_TEST_CTID") -CHALLENGE_UUID = os.getenv("CE_TEST_CHALLENGE_UUID") -ED25519_DID_KEY = os.getenv("CE_TEST_DID_KEY") -ED25519_KID = ( - f"{ED25519_DID_KEY}#{ED25519_DID_KEY[len('did:key:'):]}" - if ED25519_DID_KEY and ED25519_DID_KEY.startswith("did:key:") - else "" -) +#Test parameters +PUBLISHER_BASE = "https://fake-publisher.test" +ACCESS_TOKEN = "fake-token-for-unit-tests" +USER_ID = "00000000-0000-0000-0000-000000000001" +CTID = "ce-12345678-1234-1234-1234-123456789abc" +CHALLENGE_UUID = "11111111-2222-3333-4444-555555555555" +ED25519_DID_KEY = "did:key:z6MkhaXgBZDvf7gKKWXjGmgkbQXyrXYY4uZmTaTzPhRgg9Wm" +ED25519_KID = f"{ED25519_DID_KEY}#{ED25519_DID_KEY[len('did:key:'):]}" def _fake_response(status_code: int, parsed=None) -> SimpleNamespace: return SimpleNamespace(status_code=status_code, parsed=parsed) @@ -85,6 +80,54 @@ def to_dict(self): return dict(self._data) +@pytest.fixture(scope="session") +def ed25519_keypair(): + """Generate a fresh Ed25519 keypair for crypto tests. + + Returns (private_key_multibase, did_key, kid). The matching public + key is encoded as a did:key, so signatures created with this private + key will verify against the returned kid. + """ + try: + from cryptography.hazmat.primitives import serialization + from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + except ImportError: + pytest.skip("cryptography package not installed") + + try: + import base58 + except ImportError: + pytest.skip("base58 package not installed") + + sk = Ed25519PrivateKey.generate() + seed = sk.private_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PrivateFormat.Raw, + encryption_algorithm=serialization.NoEncryption(), + ) + pub = sk.public_key().public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw, + ) + + # did:key encoding: multicodec prefix 0xed01 (Ed25519 pub) + raw pub bytes, + # then base58btc encoded with 'z' multibase prefix. + pub_multicodec = bytes([0xED, 0x01]) + pub + did_key = "did:key:z" + base58.b58encode(pub_multicodec).decode() + + # Private key in the same multibase form your code expects. + # Adjust the multicodec prefix if did_ops uses a different encoding. + priv_multicodec = bytes([0x80, 0x26]) + seed # 0x8026 = Ed25519 priv multicodec + priv_multibase = "z" + base58.b58encode(priv_multicodec).decode() + + kid = f"{did_key}#{did_key[len('did:key:'):]}" + return priv_multibase, did_key, kid + + +# --------------------------------------------------------------------------- +# Client construction +# --------------------------------------------------------------------------- + class TestClient: def test_client_strips_trailing_slash(self): client = _client(ACCESS_TOKEN, PUBLISHER_BASE + "/", ssl_verify=False) @@ -94,7 +137,7 @@ def test_client_strips_trailing_slash(self): class TestParsedToDict: - """_parsed_to_dict centralises SDK response unwrapping; verify all 3 shapes.""" + """``_parsed_to_dict`` centralises SDK response unwrapping; verify all 3 shapes.""" def test_dict_passthrough(self): assert _parsed_to_dict({"valid": True}) == {"valid": True} @@ -118,8 +161,7 @@ def test_falsy_returns_empty_dict(self): class TestValidateCtid: def test_valid_ctid_passes(self): ok, err = validate_ctid(CTID) - assert ok - assert err == "" + assert ok and err == "" def test_empty_string_fails(self): ok, err = validate_ctid("") @@ -139,13 +181,11 @@ def test_ctid_is_case_insensitive(self): class TestValidateDate: def test_empty_value_is_treated_as_optional(self): ok, err, iso = validate_date("", "ValidFrom") - assert ok - assert err == "" and iso == "" + assert ok and err == "" and iso == "" def test_valid_date_converts_to_iso8601(self): ok, err, iso = validate_date("01/15/2024", "ValidFrom") - assert ok - assert err == "" + assert ok and err == "" assert iso == "2024-01-15T00:00:00Z" def test_iso_format_input_is_rejected(self): @@ -293,8 +333,7 @@ def test_http_error_yields_not_member_message(self): with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: mock_sync.return_value = _fake_response(403, None) ok, err = call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - assert not ok - assert err != "" + assert not ok and err != "" def test_handles_exception_gracefully(self): with patch("ce.iir.did_ops.is_user_member_sync", side_effect=RuntimeError("timeout")): @@ -305,9 +344,8 @@ def test_default_task_is_iir(self): with patch("ce.iir.did_ops.is_user_member_sync") as mock_sync: mock_sync.return_value = _fake_response(200, {"valid": True}) call_is_user_member(USER_ID, CTID, ACCESS_TOKEN, PUBLISHER_BASE, False) - args, kwargs = mock_sync.call_args - assert kwargs["task"] == "IIR" - assert kwargs["task"] == IIR_TASK + _, kwargs = mock_sync.call_args + assert kwargs["task"] == "IIR" == IIR_TASK assert kwargs["ctid"] == CTID def test_explicit_task_overrides_default(self): @@ -355,7 +393,6 @@ def test_exception_returns_api_request_failed_message(self): class TestCallGetOrganizationIIRDetail: - def test_unwraps_envelope_on_success(self): body = _ParsedObj( valid=True, @@ -529,7 +566,7 @@ def test_did_web_path_returns_vm_ids(self): assert vm_ids == ["did:web:example.com#key-1"] def test_unsupported_did_method_returns_classify_error(self): - ok, vm_ids, data, err = call_validate_did( + ok, vm_ids, _data, err = call_validate_did( "did:ethr:0x123", "", ACCESS_TOKEN, PUBLISHER_BASE, False ) assert not ok @@ -570,11 +607,15 @@ def _challenge_body(self, did=None, challenge=None): def test_returns_list_on_success(self): with patch("ce.iir.did_ops.create_challenges_sync") as mock_sync: mock_sync.return_value = _fake_response( - 200, [self._challenge_body(did=f"{ED25519_DID_KEY}#k1"), - self._challenge_body(did=f"{ED25519_DID_KEY}#k2")] + 200, + [ + self._challenge_body(did=f"{ED25519_DID_KEY}#k1"), + self._challenge_body(did=f"{ED25519_DID_KEY}#k2"), + ], ) ok, data, err = call_create_challenges( - CTID, [f"{ED25519_DID_KEY}#k1", f"{ED25519_DID_KEY}#k2"], + CTID, + [f"{ED25519_DID_KEY}#k1", f"{ED25519_DID_KEY}#k2"], ACCESS_TOKEN, PUBLISHER_BASE, False, ) assert ok and err == "" @@ -628,8 +669,7 @@ def test_success(self): ok, err = call_verify_jwt_signature( "proof.jwt.token", CHALLENGE_UUID, ACCESS_TOKEN, PUBLISHER_BASE, False ) - assert ok - assert err == "" + assert ok and err == "" def test_404_means_challenge_expired_or_missing(self): with patch("ce.iir.did_ops.validate_jwt_sync") as mock_sync: @@ -767,25 +807,11 @@ def test_did_web_to_url_with_path(self): ) -@pytest.fixture(scope="session") -def private_key() -> str: - key = os.getenv("CE_TEST_PRIVKEY_MULTIBASE", "").strip() - - if not key: - pytest.skip("CE_TEST_PRIVKEY_MULTIBASE not set in .env.test") - - try: - did_ops._extract_ed25519_seed(key) - except Exception as e: - pytest.skip(f"Invalid CE_TEST_PRIVKEY_MULTIBASE format: {e}") - - return key - class TestJwtCrypto: - def _make_payload(self) -> dict: + def _make_payload(self, did_key: str) -> dict: return { - "did": ED25519_DID_KEY, + "did": did_key, "challenge": CHALLENGE_UUID, "aud": "ce-api", "iat": 1700000000, @@ -793,27 +819,31 @@ def _make_payload(self) -> dict: "ctid": CTID, } - def test_sign_produces_a_three_part_jwt(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) + def test_sign_produces_a_three_part_jwt(self, ed25519_keypair): + priv, did_key, kid = ed25519_keypair + token = sign_proof_jwt(priv, kid, self._make_payload(did_key)) assert isinstance(token, str) assert token.count(".") == 2 - def test_sign_and_verify_roundtrip(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) - ok, err = verify_proof_jwt(token, ED25519_KID) + def test_sign_and_verify_roundtrip(self, ed25519_keypair): + priv, did_key, kid = ed25519_keypair + token = sign_proof_jwt(priv, kid, self._make_payload(did_key)) + ok, err = verify_proof_jwt(token, kid) assert ok, err - def test_tampered_signature_fails_verification(self, private_key): - token = sign_proof_jwt(private_key, ED25519_KID, self._make_payload()) + def test_tampered_signature_fails_verification(self, ed25519_keypair): + priv, did_key, kid = ed25519_keypair + token = sign_proof_jwt(priv, kid, self._make_payload(did_key)) parts = token.split(".") first = parts[2][0] parts[2] = ("B" if first != "B" else "C") + parts[2][1:] - ok, err = verify_proof_jwt(".".join(parts), ED25519_KID) + ok, err = verify_proof_jwt(".".join(parts), kid) assert not ok and "signature" in err.lower() - def test_bad_private_key_raises(self): + def test_bad_private_key_raises(self, ed25519_keypair): + _, _, kid = ed25519_keypair with pytest.raises(Exception): - sign_proof_jwt("zNOTVALID", ED25519_KID, self._make_payload()) + sign_proof_jwt("zNOTVALID", kid, self._make_payload(ED25519_DID_KEY)) class TestExtractUserIdFromToken: