From 5709f550ec70f2da274e6002f8a5be7be00b1739 Mon Sep 17 00:00:00 2001 From: aydarng Date: Wed, 21 Aug 2024 14:41:15 +0200 Subject: [PATCH 01/11] Add prefixed digest verification (#29) * Add prefixed digest verification * Fix exceptions --- src/regps/app/api/controllers.py | 9 ++++++--- src/regps/app/api/digest_verifier.py | 17 +++++++++++++++++ src/regps/app/api/exceptions.py | 5 +++++ src/regps/app/fastapi_app.py | 12 ++++++++---- 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/regps/app/api/digest_verifier.py diff --git a/src/regps/app/api/controllers.py b/src/regps/app/api/controllers.py index b8efadb..c15522f 100644 --- a/src/regps/app/api/controllers.py +++ b/src/regps/app/api/controllers.py @@ -1,6 +1,7 @@ import requests from regps.app.adapters.verifier_service_adapter import VerifierServiceAdapter -from regps.app.api.exceptions import VerifierServiceException +from regps.app.api.exceptions import VerifierServiceException, DigestVerificationFailedException +from regps.app.api.digest_verifier import verify_digest class APIController: @@ -31,8 +32,10 @@ def check_upload(self, aid: str, dig: str): raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) return verifier_response.json() - def upload(self, aid: str, dig: str, contype: str, report): - verifier_response = self.verifier_adapter.upload_request(aid, dig, contype, report) + def upload(self, aid: str, dig: str, report: bytes, contype: str, raw): + if not verify_digest(report, dig): + raise DigestVerificationFailedException("Report digest verification failed", 400) + verifier_response = self.verifier_adapter.upload_request(aid, dig, contype, raw) if verifier_response.status_code != 200: raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) return verifier_response diff --git a/src/regps/app/api/digest_verifier.py b/src/regps/app/api/digest_verifier.py new file mode 100644 index 0000000..579068d --- /dev/null +++ b/src/regps/app/api/digest_verifier.py @@ -0,0 +1,17 @@ +from hashlib import sha256 +from src.regps.app.api.exceptions import DigestVerificationFailedException + + +def get_non_prefixed_digest(dig): + try: + prefix, digest = dig.split("_", 1) + except ValueError: + raise DigestVerificationFailedException(f"Digest ({dig}) must start with prefix", 400) + return digest + + +def verify_digest(file: bytes, digest: str): + digest = get_non_prefixed_digest(digest) + actual_digest = sha256(file).hexdigest() + return actual_digest == digest + diff --git a/src/regps/app/api/exceptions.py b/src/regps/app/api/exceptions.py index 3ed0347..9161695 100644 --- a/src/regps/app/api/exceptions.py +++ b/src/regps/app/api/exceptions.py @@ -9,3 +9,8 @@ def __init__(self, detail: str, status_code: int): class VerifySignedHeadersException(HTTPException): def __init__(self, detail: str, status_code: int): super().__init__(status_code=status_code, detail=detail) + + +class DigestVerificationFailedException(HTTPException): + def __init__(self, detail: str, status_code: int): + super().__init__(status_code=status_code, detail=detail) diff --git a/src/regps/app/fastapi_app.py b/src/regps/app/fastapi_app.py index 8ff0f20..3ba3fe1 100644 --- a/src/regps/app/fastapi_app.py +++ b/src/regps/app/fastapi_app.py @@ -10,7 +10,8 @@ from starlette.middleware.cors import CORSMiddleware from regps.app.api.utils.pydantic_models import LoginRequest, LoginResponse, CheckLoginResponse, CheckUploadResponse, \ UploadResponse -from regps.app.api.exceptions import VerifierServiceException, VerifySignedHeadersException +from regps.app.api.exceptions import VerifierServiceException, VerifySignedHeadersException, \ + DigestVerificationFailedException from regps.app.api.controllers import APIController from regps.app.api.utils.swagger_examples import login_examples, check_login_examples, upload_examples, \ check_upload_examples @@ -121,10 +122,13 @@ async def upload_route(request: Request, response: Response, try: verify_signed_headers.process_request(request, aid) raw = await request.body() + form = await request.form() + upload = form.get("upload") + report = await upload.read() logger.info( f"Upload: request for {aid} {dig} {raw} {request.headers.get('Content-Type')}" ) - resp = api_controller.upload(aid, dig, request.headers.get('Content-Type'), raw) + resp = api_controller.upload(aid, dig, report, request.headers.get('Content-Type'), raw) if resp.status_code >= 400: logger.info(f"Upload: Invalid signature on report or error was received") @@ -132,12 +136,12 @@ async def upload_route(request: Request, response: Response, logger.info(f"Upload: completed upload for {aid} {dig} with code {resp.status_code}") reports[aid].append(resp.json()) return JSONResponse(status_code=200, content=resp.json()) - except VerifierServiceException or VerifySignedHeadersException as e: + except HTTPException as e: logger.error(f"Upload: Exception: {e}") response.status_code = e.status_code return JSONResponse(content=e.detail, status_code=e.status_code) except Exception as e: - logger.error(f"Upload: Exception: {e}") + logger.error(f"Upload: Unknown Exception: {e}") raise HTTPException(status_code=500, detail=str(e)) From 6e52f519907707ebfaeb4fcd324c9c12374097d3 Mon Sep 17 00:00:00 2001 From: aydarng Date: Wed, 21 Aug 2024 17:24:17 +0200 Subject: [PATCH 02/11] Feature/modify tests to use sha256 prefixed digest (#31) * Add prefixed digest verification * Fix exceptions * Fix imports * Fix github action * Fix docker compose --- .github/workflows/main.yml | 4 +++- docker-compose.yml | 4 ++-- src/regps/app/api/digest_verifier.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 792b41a..16bacb8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,9 @@ jobs: pip install flake8 pytest hio httpx pip install -r requirements.txt - name: Start dependencies - run: docker compose up -d + run: | + sudo docker compose up -d vlei-verifier + sudo docker compose up -d --build reg-pilot-api - name: Run unit tests run: pytest diff --git a/docker-compose.yml b/docker-compose.yml index 39235e4..e6a9751 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,8 +25,8 @@ services: timeout: 3s retries: 5 start_period: 2s - # depends_on: - # - verifier +# depends_on: +# - vlei-verifier vlei-verifier: build: diff --git a/src/regps/app/api/digest_verifier.py b/src/regps/app/api/digest_verifier.py index 579068d..c7a0b63 100644 --- a/src/regps/app/api/digest_verifier.py +++ b/src/regps/app/api/digest_verifier.py @@ -1,5 +1,6 @@ from hashlib import sha256 -from src.regps.app.api.exceptions import DigestVerificationFailedException +from regps.app.api.exceptions import DigestVerificationFailedException + def get_non_prefixed_digest(dig): From c4e160355e9300b2b2e29828a3fd573fb6674601 Mon Sep 17 00:00:00 2001 From: Lance Date: Wed, 21 Aug 2024 12:44:11 -0400 Subject: [PATCH 03/11] Remove verifier docker build (#32) * remove the build for verifier image Signed-off-by: 2byrds <2byrds@gmail.com> --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e6a9751..5329342 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,9 +29,6 @@ services: # - vlei-verifier vlei-verifier: - build: - context: . - dockerfile: ./images/verifier.dockerfile image: 2byrds/vlei-verifier:latest container_name: vlei-verifier hostname: vlei-verifier From fc8a906549ff54dd9c9fd6df4bbcc86b7c76cf40 Mon Sep 17 00:00:00 2001 From: Lance Date: Wed, 21 Aug 2024 15:01:17 -0400 Subject: [PATCH 04/11] Form multipart dep (#34) * added python-multipart install dependency Signed-off-by: 2byrds <2byrds@gmail.com> --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d3e4d61..c84c081 100644 --- a/setup.py +++ b/setup.py @@ -80,7 +80,8 @@ 'swagger-ui-py>=22.7.13', 'keri>=1.1.11', 'fastapi>=0.111.1', - 'requests>=2.32.3' + 'requests>=2.32.3', + 'python-multipart' ], extras_require={ # eg: From d5fd2af54ce1860bee95621cdc80b5c864360e47 Mon Sep 17 00:00:00 2001 From: aydarng Date: Thu, 22 Aug 2024 15:14:31 +0200 Subject: [PATCH 05/11] Add Unit tests for digest_verifier+ Add pytest coverage to github actions+ Add lint github action using Ruff+ Run Ruff formatter (#35) --- .github/workflows/main.yml | 14 +- .gitignore | 4 +- setup.py | 79 ++-- .../app/adapters/verifier_service_adapter.py | 49 ++- src/regps/app/api/controllers.py | 33 +- src/regps/app/api/digest_verifier.py | 6 +- src/regps/app/api/signed_headers_verifier.py | 22 +- src/regps/app/api/utils/pydantic_models.py | 57 +-- src/regps/app/api/utils/swagger_examples.py | 52 ++- src/regps/app/cli/commands/start.py | 25 +- src/regps/app/cli/regps.py | 17 +- src/regps/app/fastapi_app.py | 373 +++++++++++------- tests/integration/test_service_integration.py | 6 +- tests/unit/test_login.py | 32 +- tests/unit/test_service.py | 8 +- tests/unit/test_verifying.py | 54 ++- 16 files changed, 518 insertions(+), 313 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16bacb8..1915b80 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,14 @@ on: - "main" workflow_dispatch: jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v2 + - uses: chartboost/ruff-action@v1 + with: + args: check . test: name: Run Tests${{ matrix.keria-version }} runs-on: ${{ matrix.os }} @@ -31,13 +39,13 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 pytest hio httpx + pip install flake8 pytest pytest-cov hio httpx pip install -r requirements.txt - name: Start dependencies run: | sudo docker compose up -d vlei-verifier sudo docker compose up -d --build reg-pilot-api - - name: Run unit tests - run: pytest + - name: Run unit tests wit coverage + run: pytest --cov=./src/regps/app --cov-report=term-missing --cov-fail-under=50 diff --git a/.gitignore b/.gitignore index 28ac81c..b1a5a96 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ venv **/__pycache__ *.egg-info/ -.vscode/ \ No newline at end of file +.vscode/ +.coverage +.coverage.* \ No newline at end of file diff --git a/setup.py b/setup.py index c84c081..170d086 100644 --- a/setup.py +++ b/setup.py @@ -27,38 +27,38 @@ from setuptools import setup setup( - name='reg-pilot-api', - version='0.0.2', # also change in src/regps/__init__.py - license='Apache Software License 2.0', - description='RegPS: Regulation Portal Service API.', + name="reg-pilot-api", + version="0.0.2", # also change in src/regps/__init__.py + license="Apache Software License 2.0", + description="RegPS: Regulation Portal Service API.", long_description="RegPS: A Regulation Portal Service to orchestate web app, vLEI validation, etc.", - author='Lance Byrd', - author_email='lance.byrd@rootsid.com', - url='https://github.com/gleif-it/reg-pilot-api', - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + author="Lance Byrd", + author_email="lance.byrd@rootsid.com", + url="https://github.com/gleif-it/reg-pilot-api", + packages=find_packages("src"), + package_dir={"": "src"}, + py_modules=[splitext(basename(path))[0] for path in glob("src/*.py")], include_package_data=True, zip_safe=False, classifiers=[ # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: Unix', - 'Operating System :: POSIX', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: Implementation :: CPython', + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: Unix", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", # uncomment if you test on these interpreters: # 'Programming Language :: Python :: Implementation :: PyPy', # 'Programming Language :: Python :: Implementation :: IronPython', # 'Programming Language :: Python :: Implementation :: Jython', # 'Programming Language :: Python :: Implementation :: Stackless', - 'Topic :: Utilities', + "Topic :: Utilities", ], project_urls={ - 'Issue Tracker': 'https://github.com/gleif-it/regulation-portal-service/issues', + "Issue Tracker": "https://github.com/gleif-it/regulation-portal-service/issues", }, keywords=[ "secure attribution", @@ -67,21 +67,21 @@ "vLEI", # eg: 'keyword1', 'keyword2', 'keyword3', ], - python_requires='>=3.12.0', + python_requires=">=3.12.0", install_requires=[ - 'apispec>=6.3.0', - 'asyncio>=3.4.3', - 'dataclasses_json>=0.5.7', - 'falcon>=3.1.0', - 'gunicorn>=20.1.0', - 'uvicorn>=0.30.3', - 'http_sfv>=0.9.8', - 'requests>=2.31.0', - 'swagger-ui-py>=22.7.13', - 'keri>=1.1.11', - 'fastapi>=0.111.1', - 'requests>=2.32.3', - 'python-multipart' + "apispec>=6.3.0", + "asyncio>=3.4.3", + "dataclasses_json>=0.5.7", + "falcon>=3.1.0", + "gunicorn>=20.1.0", + "uvicorn>=0.30.3", + "http_sfv>=0.9.8", + "requests>=2.31.0", + "swagger-ui-py>=22.7.13", + "keri>=1.1.11", + "fastapi>=0.111.1", + "requests>=2.32.3", + "python-multipart" ], extras_require={ # eg: @@ -89,14 +89,13 @@ # ':python_version=="2.6"': ['argparse'], }, tests_require=[ - 'coverage>=5.5', - 'pytest>=6.2.4', - ], - setup_requires=[ + "coverage>=5.5", + "pytest>=6.2.4", ], + setup_requires=[], entry_points={ - 'console_scripts': [ - 'regps = regps.app.cli.regps:main', + "console_scripts": [ + "regps = regps.app.cli.regps:main", ] }, ) diff --git a/src/regps/app/adapters/verifier_service_adapter.py b/src/regps/app/adapters/verifier_service_adapter.py index c8d49f4..616eb4a 100644 --- a/src/regps/app/adapters/verifier_service_adapter.py +++ b/src/regps/app/adapters/verifier_service_adapter.py @@ -1,4 +1,3 @@ -import os from time import sleep import json import logging @@ -20,28 +19,45 @@ class VerifierServiceAdapter: def __init__(self): # TODO: take only base url for the verifier service from the environment variable - self.auths_url = os.environ.get('VERIFIER_AUTHORIZATIONS', "http://127.0.0.1:7676/authorizations/") - self.presentations_url = os.environ.get('VERIFIER_PRESENTATIONS', "http://127.0.0.1:7676/presentations/") - self.reports_url = os.environ.get('VERIFIER_REPORTS', "http://127.0.0.1:7676/reports/") - self.request_url = os.environ.get('VERIFIER_REQUESTS', "http://localhost:7676/request/verify/") + self.auths_url = os.environ.get( + "VERIFIER_AUTHORIZATIONS", "http://127.0.0.1:7676/authorizations/" + ) + self.presentations_url = os.environ.get( + "VERIFIER_PRESENTATIONS", "http://127.0.0.1:7676/presentations/" + ) + self.reports_url = os.environ.get( + "VERIFIER_REPORTS", "http://127.0.0.1:7676/reports/" + ) + self.request_url = os.environ.get( + "VERIFIER_REQUESTS", "http://localhost:7676/request/verify/" + ) def check_login_request(self, aid: str) -> requests.Response: logger.info(f"checking login: {aid}") logger.info(f"getting from {self.auths_url}{aid}") - res = requests.get(f"{self.auths_url}{aid}", headers={"Content-Type": "application/json"}) + res = requests.get( + f"{self.auths_url}{aid}", headers={"Content-Type": "application/json"} + ) logger.info(f"login status: {json.dumps(res.json())}") return res def verify_vlei_request(self, said: str, vlei: str) -> requests.Response: logger.info(f"Verify vlei task started {said} {vlei[:50]}") logger.info(f"presenting vlei ecr to url {self.presentations_url}{said}") - res = requests.put(f"{self.presentations_url}{said}", headers={"Content-Type": "application/json+cesr"}, - data=vlei) + res = requests.put( + f"{self.presentations_url}{said}", + headers={"Content-Type": "application/json+cesr"}, + data=vlei, + ) logger.info(f"verify vlei task response {json.dumps(res.json())}") return res def verify_cig_request(self, aid, cig, ser) -> requests.Response: - logger.info("Verify header sig started aid = {}, cig = {}, ser = {}....".format(aid, cig, ser)) + logger.info( + "Verify header sig started aid = {}, cig = {}, ser = {}....".format( + aid, cig, ser + ) + ) logger.info("posting to {}".format(self.request_url + f"{aid}")) res = requests.post(self.request_url + aid, params={"sig": cig, "data": ser}) logger.info(f"Verify sig response {json.dumps(res.json())}") @@ -50,11 +66,16 @@ def verify_cig_request(self, aid, cig, ser) -> requests.Response: def check_upload_request(self, aid: str, dig: str) -> requests.Response: logger.info(f"checking upload: aid {aid} and dig {dig}") logger.info(f"getting from {self.reports_url}{aid}/{dig}") - res = requests.get(f"{self.reports_url}{aid}/{dig}", headers={"Content-Type": "application/json"}) + res = requests.get( + f"{self.reports_url}{aid}/{dig}", + headers={"Content-Type": "application/json"}, + ) logger.info(f"upload status: {json.dumps(res.json())}") return res - def upload_request(self, aid: str, dig: str, contype: str, report) -> requests.Response: + def upload_request( + self, aid: str, dig: str, contype: str, report + ) -> requests.Response: logger.info(f"upload report type {type(report)}") # first check to see if we've already uploaded cres = self.check_upload_request(aid, dig) @@ -63,7 +84,11 @@ def upload_request(self, aid: str, dig: str, contype: str, report) -> requests.R return cres else: logger.info(f"upload posting to {self.reports_url}{aid}/{dig}") - cres = requests.post(f"{self.reports_url}{aid}/{dig}", headers={"Content-Type": contype}, data=report) + cres = requests.post( + f"{self.reports_url}{aid}/{dig}", + headers={"Content-Type": contype}, + data=report, + ) logger.info(f"post response {json.dumps(cres.json())}") if cres.status_code < 300: cres = self.check_upload_request(aid, dig) diff --git a/src/regps/app/api/controllers.py b/src/regps/app/api/controllers.py index c15522f..1b9cb90 100644 --- a/src/regps/app/api/controllers.py +++ b/src/regps/app/api/controllers.py @@ -1,6 +1,9 @@ import requests from regps.app.adapters.verifier_service_adapter import VerifierServiceAdapter -from regps.app.api.exceptions import VerifierServiceException, DigestVerificationFailedException +from regps.app.api.exceptions import ( + VerifierServiceException, + DigestVerificationFailedException, +) from regps.app.api.digest_verifier import verify_digest @@ -9,33 +12,47 @@ def __init__(self): self.verifier_adapter = VerifierServiceAdapter() def check_login(self, aid: str): - verifier_response: requests.Response = self.verifier_adapter.check_login_request(aid) + verifier_response: requests.Response = ( + self.verifier_adapter.check_login_request(aid) + ) if verifier_response.status_code != 200: - raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) + raise VerifierServiceException( + verifier_response.json(), verifier_response.status_code + ) return verifier_response.json() def login(self, said: str, vlei: str): verifier_response = self.verifier_adapter.verify_vlei_request(said, vlei) if verifier_response.status_code != 200: - raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) + raise VerifierServiceException( + verifier_response.json(), verifier_response.status_code + ) return verifier_response.json() def verify_cig(self, aid, cig, ser): verifier_response = self.verifier_adapter.verify_cig_request(aid, cig, ser) if verifier_response.status_code != 202: - raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) + raise VerifierServiceException( + verifier_response.json(), verifier_response.status_code + ) return verifier_response.json() def check_upload(self, aid: str, dig: str): verifier_response = self.verifier_adapter.check_upload_request(aid, dig) if verifier_response.status_code != 200: - raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) + raise VerifierServiceException( + verifier_response.json(), verifier_response.status_code + ) return verifier_response.json() def upload(self, aid: str, dig: str, report: bytes, contype: str, raw): if not verify_digest(report, dig): - raise DigestVerificationFailedException("Report digest verification failed", 400) + raise DigestVerificationFailedException( + "Report digest verification failed", 400 + ) verifier_response = self.verifier_adapter.upload_request(aid, dig, contype, raw) if verifier_response.status_code != 200: - raise VerifierServiceException(verifier_response.json(), verifier_response.status_code) + raise VerifierServiceException( + verifier_response.json(), verifier_response.status_code + ) return verifier_response diff --git a/src/regps/app/api/digest_verifier.py b/src/regps/app/api/digest_verifier.py index c7a0b63..a3e0a8c 100644 --- a/src/regps/app/api/digest_verifier.py +++ b/src/regps/app/api/digest_verifier.py @@ -2,12 +2,13 @@ from regps.app.api.exceptions import DigestVerificationFailedException - def get_non_prefixed_digest(dig): try: prefix, digest = dig.split("_", 1) except ValueError: - raise DigestVerificationFailedException(f"Digest ({dig}) must start with prefix", 400) + raise DigestVerificationFailedException( + f"Digest ({dig}) must start with prefix", 400 + ) return digest @@ -15,4 +16,3 @@ def verify_digest(file: bytes, digest: str): digest = get_non_prefixed_digest(digest) actual_digest = sha256(file).hexdigest() return actual_digest == digest - diff --git a/src/regps/app/api/signed_headers_verifier.py b/src/regps/app/api/signed_headers_verifier.py index a6477fb..a95a20f 100644 --- a/src/regps/app/api/signed_headers_verifier.py +++ b/src/regps/app/api/signed_headers_verifier.py @@ -31,7 +31,11 @@ def process_request(self, req: Request, raid): return res else: raise VerifySignedHeadersException( - json.dumps({"msg": f"Header AID {aid} does not match request {raid}"}), 401) + json.dumps( + {"msg": f"Header AID {aid} does not match request {raid}"} + ), + 401, + ) except VerifySignedHeadersException as e: raise e @@ -40,19 +44,27 @@ def handle_headers(req): logger.info(f"processing header req {req}") headers = req.headers - if "SIGNATURE-INPUT" not in headers or "SIGNATURE" not in headers or "SIGNIFY-RESOURCE" not in headers or "SIGNIFY-TIMESTAMP" not in headers: - raise VerifySignedHeadersException(json.dumps({"msg": f"Incorrect Headers"}), 401) + if ( + "SIGNATURE-INPUT" not in headers + or "SIGNATURE" not in headers + or "SIGNIFY-RESOURCE" not in headers + or "SIGNIFY-TIMESTAMP" not in headers + ): + raise VerifySignedHeadersException( + json.dumps({"msg": "Incorrect Headers"}), 401 + ) siginput = headers["SIGNATURE-INPUT"] signature = headers["SIGNATURE"] resource = headers["SIGNIFY-RESOURCE"] - timestamp = headers["SIGNIFY-TIMESTAMP"] inputs = ending.desiginput(siginput.encode("utf-8")) inputs = [i for i in inputs if i.name == "signify"] if not inputs: - raise VerifySignedHeadersException(json.dumps({"msg": f"Incorrect Headers"}), 401) + raise VerifySignedHeadersException( + json.dumps({"msg": "Incorrect Headers"}), 401 + ) for inputage in inputs: items = [] diff --git a/src/regps/app/api/utils/pydantic_models.py b/src/regps/app/api/utils/pydantic_models.py index e40e61f..ccc7844 100644 --- a/src/regps/app/api/utils/pydantic_models.py +++ b/src/regps/app/api/utils/pydantic_models.py @@ -1,34 +1,45 @@ -from fastapi import Path, Header +from fastapi import Header from pydantic import BaseModel, Field -from regps.app.api.utils.swagger_examples import login_examples, check_login_examples, upload_examples +from regps.app.api.utils.swagger_examples import ( + login_examples, + check_login_examples, + upload_examples, +) class SignedHeaders(BaseModel): - signature: str = Header(openapi_examples={ - "default": { - "summary": "Default signature", - "value": upload_examples["request"]["headers"]["signature"], + signature: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature", + "value": upload_examples["request"]["headers"]["signature"], + } } - }) - signature_input: str = Header(openapi_examples={ - "default": { - "summary": "Default signature_input", - "value": upload_examples["request"]["headers"]["signature_input"], + ) + signature_input: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature_input", + "value": upload_examples["request"]["headers"]["signature_input"], + } } - }) - signify_resource: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_resource", - "value": upload_examples["request"]["headers"]["signify_resource"], + ) + signify_resource: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_resource", + "value": upload_examples["request"]["headers"]["signify_resource"], + } } - }) - signify_timestamp: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_timestamp", - "value": upload_examples["request"]["headers"]["signify_timestamp"], + ) + signify_timestamp: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_timestamp", + "value": upload_examples["request"]["headers"]["signify_timestamp"], + } } - }) - + ) class LoginRequest(BaseModel): diff --git a/src/regps/app/api/utils/swagger_examples.py b/src/regps/app/api/utils/swagger_examples.py index 9c0c76d..d869610 100644 --- a/src/regps/app/api/utils/swagger_examples.py +++ b/src/regps/app/api/utils/swagger_examples.py @@ -1,23 +1,24 @@ login_examples = { "request": { "said": ["EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk"], - "vlei": ["""{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DBZxSKiTUaSiWTrkAOAMk_WyCivZUbwMFIJ-vZZHF7D5\"],\"nt\":\"1\",\"n\":[\"ELgz3XamjqxCNWe7rvih9UAzB-1dH7-zBgW8E7RF3OeJ\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABidds3sVtwYGBKl1iyLY9SVpKDk65FoWS48gWUjjhvIcn0Jne-C6ZJd9zi_c6ioNmK1IVkkfTjbqZKVxSgmDgN-BADAAD5CkTmodWH9yR-tcQ_I2l1SJWxFQxpn2fCU4BmgjLt06gNMtpGetiFyINg_t0n-o9VS3ioW8Rs3DXMclrh7HIOABAFKfy1eQ9i6Hk8bORdkW5uwGg3LQrZoUSTnj_2i3TCH45EPTJVnkjObUAYZ1_AQWVPdouVj0Am0PUJPwT1JNsLACCKgFfpBxm8aQQdqAgwp2INz6w9WZ4oJAe_cUxEAqMnnTKzP8eRcj5JNCbzEN8wWNdh5CB5Y6OTlanTxl46--AI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d795649p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"1\",\"p\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"a\":[{\"i\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"s\":\"0\",\"d\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\"}]}-VBq-AABAACXy8MOpF7kY3AxtHTbD__kva6VnLI9ya9Niz6N9vnbWgA5zFc15BPgEFE6kAwonHIix6LXrVKpJB59k0oE_5oE-BADAABp3uFwoL25z46tu1--4oz0hkoAdywAjT6Ojj2IKGk7WJhmCzXPxBsV02546HD5mEqqg6NqWtuv7vyZJgivZZ8EABCvL4-71heQ9S3I8ZaAc-gJnwpSM3oKOpspCas94mBuwrRBu4XWoscMMpKR6j6a9hKlPcdRbkehmAoRRO9HyhcLACAzxTDXn8FegSMumEpg7IE7xipLaPF6qgY1POCVALBTXB7oBUPgf7yr1ooXkCTQ8tEfSm0r5LM18UJOS74KTvcI-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d676775p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EOflFmKcSNvILfVWT6maf56_PhfrYoTy5p5yIkmv4jvs\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"2\",\"p\":\"EAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m\",\"a\":[{\"i\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"0\",\"d\":\"EJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon\"}]}-VBq-AABAADH7raYUgVuo4xNT4P6cuXkik7HkETKGauy0_pJzAR_QGuSROg4PxDLnVjnDxoR3FeTNnav3Sug5FNY_0JH8yUP-BADAAA6hMLoDDixEGSYzK4abi__-24-c0L0G6fdWKZGfiRIlLoMCFVfRicHTn2am4-kaRMZg1O04L3VZ4fIAHgHnu8OABCHAdvvoiATpku1wskGpYR51NVv23N_8KIyeKs4nGDrwkTV3NYFj-m870LBDOXq5oKPYTWEUtZQzB9MGR8p4z0JACDghx2nEpMd8m_vt1wa3y5rbP156DTsxr25ZL2US2M57x5IJoYVqc-CBMUYdYbCa-zZtgXFCs6M6utkE7Er1vMJ-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d710371p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DAfu3nimJGpxVlNmrKUo3c7ZeP-dycDXyPy6U-Lz26jp\"],\"nt\":\"1\",\"n\":[\"EMQw8RvFgkipzpdIaRW02ejSG-05plHrRCglcDrmwD8u\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAADTjXCCg44hkPpfuC-Eu3EDcoxRN9vMnprVBDMfUwFjE3I7AmD07tKqz9F94NjM_ydWi8erDW0vTJM2DHpEYtYL-BADAAC55GJFxVmnRxUGLVC2McbqRA-s7HQqneO7Mnisk7812QlPLj2hy2kPNfyEjp6jeyXV9tGAjGbKWt58WnlmAFEBABARHPH340biYzu2TUohaSjPvXbZMNToGChDSgMKRbt7TpO7PCk46MEFL8BCb4N4v8OAC7Z6ACjL0vqgVncqyo8BACCz6X7GQUD2W7TkW_3V7RCPK8X-IosNsr2mZqu5PNZi7kYWZi2VF-KB_FCjzh3zSAyeFKRVte9o5HbxqM6H3vkI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d757209p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"1\",\"p\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"a\":[{\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"0\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\"}]}-VBq-AABAADcd2ApP_32GJSwJhMj8uU2YPN7KNwoYq8lbV1cRR6sYRJj-Kz5kUMeuXTrrqN4n3SOyML9eAPXPB__WPCvHqAF-BADAAB1pJ0n6mMD577jvKTlsVy3xoJMyK4GlNLKKmZgb8bBo8hHUft3aWDDXaMdOTyZ2YQk3vO4LJVEcznE2uecMGIMABCy0tge4t3DlGNd1s5X1zUsEKQVCt7OYtcsI88Zem7AEP4Pkh6A7pWZ1DIlk3cFdYC--jc1sUWKGmMOP95WSLEPACA4hcWCGyeUSVCfLrfXAR5lmQlB7cSxYwhTId2_Rmxh-KIPLHYpcrvyiXknQZeDTzIdLsc9kvhv8hGim_kl6sEK-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d380030p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"2\",\"p\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"a\":[{\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\"}]}-VBq-AABAABrODkziLBApmEjZsTZhij8_2WzFqXDCcBYp-Vz5tg0vuMOlq0yP-gN6kfhw9UD8x0FryplrPNCDD0eKNClgCUC-BADAACUv_4vyoQJWhm-ruioMY7RLyjFpncjEz6_NDRu48IJVmS6dFJGfRBNp1G_LlegcfsMoKkPx71hvXg1_tfYq9gJABDaQGYRc4jUEtD-k9AUl_vs574fdUi3Bf_N0zxKhEbsjAugUrtQHEVJcT9cB4eUffdPc6bSXis0YQ10Z55mFT8EACBXwhZYi11Tmt8lXvjhXaKDixHWOFkLnbBR2XpSy3we936cyLWFMFG-q0AY_CHaVTdzYJy_OS850GYBwNOsyZYF-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d410786p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"3\",\"p\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"a\":[{\"i\":\"EHX1GUC8HuRzQAF1iVs3-rM1960U99q1q7bKQItPU1gN\",\"s\":\"0\",\"d\":\"EKyOm3y9JM9SNFdLIlBG_VTfjejpKEE2KgEnPKawvuab\"}]}-VBq-AABAABJGMeW1N18v0tvhuYUe27QZL1OvhDRJhVIo7qKMc9gd4NbOzXWjuLPcPZtcvKkvTFb66l6otC41Varj19VRjcM-BADAAADWQ64oLPFhhlZYbONLFYbsQl_nChHdmPmsD9zpYFXbnEB2kzhCrMGdOdtSqW0Nb7Rezogmg969qeDj_9Lo0cMABBEjMp3Fqel1YiCRvfTZ3eJMUt15HRSyC8N-QQFctaEViPctD0U5GDa_n78PaSXPv_-dcHtIk1hp4ORHYGNLyMPACCZUVpA6S4u5xugTO2izUp-5Wit7ngDWZnkmo4XHmjedEVlm1LmoLz_w7fSg9dCxv9ihr5Z02I47jR0tBEcURUE-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c10c04d723024p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EPQYOdd-c0fjHvkGmFqO0fMTBnZBDDnxZ6REUB_QH_no\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"4\",\"p\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"a\":[{\"i\":\"EKXFyihmxElIC7NzFhV2eFXfKiL9iBk1kc5yNU7Hxw13\",\"s\":\"0\",\"d\":\"EFmAdnmvgKAVccDdnMT21jyp7x_hObyAT0ok5YCJjfLj\"}]}-VBq-AABAAD6ti3L4CI7-Nw9oZ9r3U75EFo1J2s6ekJBy08jiLHrFd3z41ZaYpHo4PmVuB1u5oOWbmcQjnqm8Y40hcYIygIL-BADAAB1CEybl44qmmMWJ22rYkCuZ0ww70Ae8OqdqhU-hyReQOEG9VwdajI_HBfwtNbUOyaExvCOfHCMwfXI8dmcaC0HABCi0K4Y7O5KW7YjyERIzQTcoPeu6pbtyC8EXx1JdNCum-CBMtIB2mTihW5tIF1RGFYmaxorIKzfSbIrkGAgtSkJACAtcjAjhqh5DJlWh_MzhcLepx7wgFlA9ZDWngB2uEtLDXVVS41QyXKtBsHoOJntdic1nnXMOoD2r-zHqjzzM-EC-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c13d974476p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"i\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"ii\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AMvLdXXYFhqV93b7mZIXHlqGygDmYryUEz5WR_hoI6go\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon\",\"i\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"0\",\"ri\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"dt\":\"2024-05-02T18:09:46.265000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACEOflFmKcSNvILfVWT6maf56_PhfrYoTy5p5yIkmv4jvs{\"v\":\"ACDC10JSON000197_\",\"d\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"ri\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"s\":\"EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao\",\"a\":{\"d\":\"ECw1gKAF2S2z9j9jlMxa0SsMkX-Okr2MxCKCQKMQUz1O\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"LEI\":\"254900OPPU84GM83MG36\",\"dt\":\"2024-05-02T18:09:46.265000+00:00\"}}-IABEKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE0AAAAAAAAAAAAAAAAAAAAAAAEJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DAfu3nimJGpxVlNmrKUo3c7ZeP-dycDXyPy6U-Lz26jp\"],\"nt\":\"1\",\"n\":[\"EMQw8RvFgkipzpdIaRW02ejSG-05plHrRCglcDrmwD8u\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAADTjXCCg44hkPpfuC-Eu3EDcoxRN9vMnprVBDMfUwFjE3I7AmD07tKqz9F94NjM_ydWi8erDW0vTJM2DHpEYtYL-BADAAC55GJFxVmnRxUGLVC2McbqRA-s7HQqneO7Mnisk7812QlPLj2hy2kPNfyEjp6jeyXV9tGAjGbKWt58WnlmAFEBABARHPH340biYzu2TUohaSjPvXbZMNToGChDSgMKRbt7TpO7PCk46MEFL8BCb4N4v8OAC7Z6ACjL0vqgVncqyo8BACCz6X7GQUD2W7TkW_3V7RCPK8X-IosNsr2mZqu5PNZi7kYWZi2VF-KB_FCjzh3zSAyeFKRVte9o5HbxqM6H3vkI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d757209p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"1\",\"p\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"a\":[{\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"0\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\"}]}-VBq-AABAADcd2ApP_32GJSwJhMj8uU2YPN7KNwoYq8lbV1cRR6sYRJj-Kz5kUMeuXTrrqN4n3SOyML9eAPXPB__WPCvHqAF-BADAAB1pJ0n6mMD577jvKTlsVy3xoJMyK4GlNLKKmZgb8bBo8hHUft3aWDDXaMdOTyZ2YQk3vO4LJVEcznE2uecMGIMABCy0tge4t3DlGNd1s5X1zUsEKQVCt7OYtcsI88Zem7AEP4Pkh6A7pWZ1DIlk3cFdYC--jc1sUWKGmMOP95WSLEPACA4hcWCGyeUSVCfLrfXAR5lmQlB7cSxYwhTId2_Rmxh-KIPLHYpcrvyiXknQZeDTzIdLsc9kvhv8hGim_kl6sEK-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d380030p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"2\",\"p\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"a\":[{\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\"}]}-VBq-AABAABrODkziLBApmEjZsTZhij8_2WzFqXDCcBYp-Vz5tg0vuMOlq0yP-gN6kfhw9UD8x0FryplrPNCDD0eKNClgCUC-BADAACUv_4vyoQJWhm-ruioMY7RLyjFpncjEz6_NDRu48IJVmS6dFJGfRBNp1G_LlegcfsMoKkPx71hvXg1_tfYq9gJABDaQGYRc4jUEtD-k9AUl_vs574fdUi3Bf_N0zxKhEbsjAugUrtQHEVJcT9cB4eUffdPc6bSXis0YQ10Z55mFT8EACBXwhZYi11Tmt8lXvjhXaKDixHWOFkLnbBR2XpSy3we936cyLWFMFG-q0AY_CHaVTdzYJy_OS850GYBwNOsyZYF-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d410786p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"3\",\"p\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"a\":[{\"i\":\"EHX1GUC8HuRzQAF1iVs3-rM1960U99q1q7bKQItPU1gN\",\"s\":\"0\",\"d\":\"EKyOm3y9JM9SNFdLIlBG_VTfjejpKEE2KgEnPKawvuab\"}]}-VBq-AABAABJGMeW1N18v0tvhuYUe27QZL1OvhDRJhVIo7qKMc9gd4NbOzXWjuLPcPZtcvKkvTFb66l6otC41Varj19VRjcM-BADAAADWQ64oLPFhhlZYbONLFYbsQl_nChHdmPmsD9zpYFXbnEB2kzhCrMGdOdtSqW0Nb7Rezogmg969qeDj_9Lo0cMABBEjMp3Fqel1YiCRvfTZ3eJMUt15HRSyC8N-QQFctaEViPctD0U5GDa_n78PaSXPv_-dcHtIk1hp4ORHYGNLyMPACCZUVpA6S4u5xugTO2izUp-5Wit7ngDWZnkmo4XHmjedEVlm1LmoLz_w7fSg9dCxv9ihr5Z02I47jR0tBEcURUE-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c10c04d723024p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EPQYOdd-c0fjHvkGmFqO0fMTBnZBDDnxZ6REUB_QH_no\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"4\",\"p\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"a\":[{\"i\":\"EKXFyihmxElIC7NzFhV2eFXfKiL9iBk1kc5yNU7Hxw13\",\"s\":\"0\",\"d\":\"EFmAdnmvgKAVccDdnMT21jyp7x_hObyAT0ok5YCJjfLj\"}]}-VBq-AABAAD6ti3L4CI7-Nw9oZ9r3U75EFo1J2s6ekJBy08jiLHrFd3z41ZaYpHo4PmVuB1u5oOWbmcQjnqm8Y40hcYIygIL-BADAAB1CEybl44qmmMWJ22rYkCuZ0ww70Ae8OqdqhU-hyReQOEG9VwdajI_HBfwtNbUOyaExvCOfHCMwfXI8dmcaC0HABCi0K4Y7O5KW7YjyERIzQTcoPeu6pbtyC8EXx1JdNCum-CBMtIB2mTihW5tIF1RGFYmaxorIKzfSbIrkGAgtSkJACAtcjAjhqh5DJlWh_MzhcLepx7wgFlA9ZDWngB2uEtLDXVVS41QyXKtBsHoOJntdic1nnXMOoD2r-zHqjzzM-EC-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c13d974476p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DJK74-FQwLUyYcopt5R5soei4Eqdoq0ucXSqC6ynYkvA\"],\"nt\":\"1\",\"n\":[\"EFpMnM4SPlishuuMS-fonwSD0LLBmTnIdgQjG5LuXW30\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABSYMBKbhVWAC1rYAIgf800CJ7Oylr9MvGVtKgoYcquCe94w6gjuTSzTqfrbjQqy-PN4TPxVtI5rnWQJR70kj4E-BADAADJJcHNEe41Thz2MU5WXj78pcto-C0lJVtC34T4rrbH2jf7i2zRFzsbHY8jLcnzdoINTm5wJunE_FWdFaTJQ9QNABCdkgIlJM0h9_EesHn1yzSBnaTch4959sUrKanXjAZ-qiXkjQ3lpg9UPyNweo9GIRN92-KIFv6xBCYIKFn0p5wLACDMKP_62CSxJ9KyNbaPAjoPGwiIUNFD5Mwu6tmUYYMrKibYqjvIrA5s_YXUFPOA3tYhsZqmm2kWOUTPLlpTqTUC-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d734341p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"1\",\"p\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"a\":[{\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"0\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\"}]}-VBq-AABAADVkgj4VZWQyrRzbUFm4vwjqhf4V-czR-HYlFLVRqXszQ4vFq4bP8UaWNFBHtMjUEbJKHvWKvF5H8236uswdgkM-BADAACJ0pvCwXBEsxG5MkPeHRGMmojxfTOS-JZQ8Qz8CSgptAUd3tbelPT0jIQNeQmqAsXxvVIXDVIqmbeDZYMfQwAIABCRs9myd_SbYLTAdBhu6cmnpp9K5bLX0vMrrmxwY5G_iDO3Dvigg2VFLTdZ6VPnVMl-B7bDcv6iWKFHZWF_0X8PACCsGXF4DmCVFBJal4mUPk9t4HPAFyB9miv1mcg73Bki-T4SfoDJ4A2nifZU4O5KtotYCmSdznamY0BsYkD2LNoD-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d224186p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"2\",\"p\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"a\":[{\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\"}]}-VBq-AABAAAllpN9qv5xOx9hE4SDS1VanWzqB8CD_k1y-vvlH487CoTTrPTm8Di1OICl0zpWBGh9LJqDCIWAnlG_cO992hYI-BADAAC7cIVpeaE7xsOhyEG3lGrqHw6rXQsQdhFaWNujGnLSTBI23ZyhsZ8bDdFwZGNb8woy9_arIwZlhOXdIPxOXvwOABCri66kCiEkExor7l1JQz-kR2bHgx3UENtDr_GiuZ3xc7hbQPo0dhXCbjtuw_enEdYzb5IsKKu0On8lmnAvAEcGACBYBDs8TZTPWFFlvoyiVf5oOUExGPFb6v93BnFitMMoGPmixEvqsRedWxuMmxKD7Bq1dqGFKE6G9kNORQ7Kx-8F-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d256392p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"3\",\"p\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"a\":[{\"i\":\"EAwFSM6lx6AE9XG-r1_PBQi0aVhZms3uQ4svV-Ao0k0u\",\"s\":\"0\",\"d\":\"EAFlIYst1qwaYxkCdaQi8X-HwZYn1KF4P-8lWyIE00Pr\"}]}-VBq-AABAAAC5KZgiI38wZ7KEaAY-M6nG5PSpasuv7Dc0PKl8_ZhAaa7o8xJY7oOSzTE8IbRjUwJ1riLmOG5s-dvrwDGt5QA-BADAACnA2pacEht173RaEX7XkPuizhcL0ZVnz28UAF0eWa97Yv6h_B1wFFuR8b-EqJhZKBkp6twrf5wEsfnS4_QJbsDABDD-x6OxRuBgCx_1EBJ0fuCyQse0S_RViHm78lcF9wwnMOer4puQfbI-PXcRyYF9w8dX1xMTzX0q21OiTYfvggIACBA3OSnnD_2_IoJXeLvk_blg3-PSihOf-rv6HEwzKYql-cYCcRgFUivZiM8bTwxokme-QNaV3cY2_c-tShUuzgC-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c09c59d325680p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EJqQeIjHV-uPfCUCgLBeJI8k85u8GKl35IeaR4YZ6RIc\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"4\",\"p\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"a\":[{\"i\":\"EF8wfC9k3VQPIUcpTbKLWyC7nXRcztDArGMYdfBntW-h\",\"s\":\"0\",\"d\":\"EG9GFCb1q2P0PJ42TK9BbjOes8FCZgpHguAfMxkdrtZi\"}]}-VBq-AABAABOqvKRb57P_j19Vl0HVHfDUbdLWPz7yMRLDKsCYTSy9P2myul5JtFDAV9Zkp449R3_QGfcPVP4s8ElUMe509YP-BADAABHq9F-pc7mvpb7gy20lgoMpQZp1CutxshKYqvJaPkZfKfG50cIS_dPvQByjb3ymL7yIYuA5iO7gNR5ek-oMFgOABDsE7pwlpVNXWXxGNh-P4r9Rz1ibHZtA9cgQ-rXwH_QyhjZeBQahaTcq1t8vVEt84lg-y-inLe2d6F1ju0rgAQPACAnrw-CX37k54JvkKMwu1nH6ogJUL-1ltxV1JyPgeh7VBbGmZH09-tvKrH7tyRCXRm0Rni2GNY4qj9EWstX5REB-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c14d192298p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"ii\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AE5fEuU0vYME2TacvBnNQ2mMHaurTSwXNKvttHOKfOiK\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\",\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"ri\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"dt\":\"2024-05-02T18:09:48.374000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACEB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros{\"v\":\"ACDC10JSON0005c8_\",\"d\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"ri\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY\",\"a\":{\"d\":\"ENIkuwNRRNS2pq_B8H32qCnZDB51VA9JG0MFa7yjZUgw\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"LEI\":\"875500ELOZEL05BVXV37\",\"dt\":\"2024-05-02T18:09:48.374000+00:00\"},\"e\":{\"d\":\"EJuWUqd2JBMzzXItL-FW5T58N86_NC1049syzsXrnVgF\",\"qvi\":{\"n\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao\"}},\"r\":{\"d\":\"EGZ97EjPSINR-O-KHDN_uw4fdrTxeuRXrqT5ZHHQJujQ\",\"usageDisclaimer\":{\"l\":\"Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.\"},\"issuanceDisclaimer\":{\"l\":\"All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.\"}}}-IABEDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI0AAAAAAAAAAAAAAAAAAAAAAAEDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DJK74-FQwLUyYcopt5R5soei4Eqdoq0ucXSqC6ynYkvA\"],\"nt\":\"1\",\"n\":[\"EFpMnM4SPlishuuMS-fonwSD0LLBmTnIdgQjG5LuXW30\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABSYMBKbhVWAC1rYAIgf800CJ7Oylr9MvGVtKgoYcquCe94w6gjuTSzTqfrbjQqy-PN4TPxVtI5rnWQJR70kj4E-BADAADJJcHNEe41Thz2MU5WXj78pcto-C0lJVtC34T4rrbH2jf7i2zRFzsbHY8jLcnzdoINTm5wJunE_FWdFaTJQ9QNABCdkgIlJM0h9_EesHn1yzSBnaTch4959sUrKanXjAZ-qiXkjQ3lpg9UPyNweo9GIRN92-KIFv6xBCYIKFn0p5wLACDMKP_62CSxJ9KyNbaPAjoPGwiIUNFD5Mwu6tmUYYMrKibYqjvIrA5s_YXUFPOA3tYhsZqmm2kWOUTPLlpTqTUC-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d734341p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"1\",\"p\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"a\":[{\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"0\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\"}]}-VBq-AABAADVkgj4VZWQyrRzbUFm4vwjqhf4V-czR-HYlFLVRqXszQ4vFq4bP8UaWNFBHtMjUEbJKHvWKvF5H8236uswdgkM-BADAACJ0pvCwXBEsxG5MkPeHRGMmojxfTOS-JZQ8Qz8CSgptAUd3tbelPT0jIQNeQmqAsXxvVIXDVIqmbeDZYMfQwAIABCRs9myd_SbYLTAdBhu6cmnpp9K5bLX0vMrrmxwY5G_iDO3Dvigg2VFLTdZ6VPnVMl-B7bDcv6iWKFHZWF_0X8PACCsGXF4DmCVFBJal4mUPk9t4HPAFyB9miv1mcg73Bki-T4SfoDJ4A2nifZU4O5KtotYCmSdznamY0BsYkD2LNoD-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d224186p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"2\",\"p\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"a\":[{\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\"}]}-VBq-AABAAAllpN9qv5xOx9hE4SDS1VanWzqB8CD_k1y-vvlH487CoTTrPTm8Di1OICl0zpWBGh9LJqDCIWAnlG_cO992hYI-BADAAC7cIVpeaE7xsOhyEG3lGrqHw6rXQsQdhFaWNujGnLSTBI23ZyhsZ8bDdFwZGNb8woy9_arIwZlhOXdIPxOXvwOABCri66kCiEkExor7l1JQz-kR2bHgx3UENtDr_GiuZ3xc7hbQPo0dhXCbjtuw_enEdYzb5IsKKu0On8lmnAvAEcGACBYBDs8TZTPWFFlvoyiVf5oOUExGPFb6v93BnFitMMoGPmixEvqsRedWxuMmxKD7Bq1dqGFKE6G9kNORQ7Kx-8F-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d256392p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"3\",\"p\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"a\":[{\"i\":\"EAwFSM6lx6AE9XG-r1_PBQi0aVhZms3uQ4svV-Ao0k0u\",\"s\":\"0\",\"d\":\"EAFlIYst1qwaYxkCdaQi8X-HwZYn1KF4P-8lWyIE00Pr\"}]}-VBq-AABAAAC5KZgiI38wZ7KEaAY-M6nG5PSpasuv7Dc0PKl8_ZhAaa7o8xJY7oOSzTE8IbRjUwJ1riLmOG5s-dvrwDGt5QA-BADAACnA2pacEht173RaEX7XkPuizhcL0ZVnz28UAF0eWa97Yv6h_B1wFFuR8b-EqJhZKBkp6twrf5wEsfnS4_QJbsDABDD-x6OxRuBgCx_1EBJ0fuCyQse0S_RViHm78lcF9wwnMOer4puQfbI-PXcRyYF9w8dX1xMTzX0q21OiTYfvggIACBA3OSnnD_2_IoJXeLvk_blg3-PSihOf-rv6HEwzKYql-cYCcRgFUivZiM8bTwxokme-QNaV3cY2_c-tShUuzgC-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c09c59d325680p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EJqQeIjHV-uPfCUCgLBeJI8k85u8GKl35IeaR4YZ6RIc\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"4\",\"p\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"a\":[{\"i\":\"EF8wfC9k3VQPIUcpTbKLWyC7nXRcztDArGMYdfBntW-h\",\"s\":\"0\",\"d\":\"EG9GFCb1q2P0PJ42TK9BbjOes8FCZgpHguAfMxkdrtZi\"}]}-VBq-AABAABOqvKRb57P_j19Vl0HVHfDUbdLWPz7yMRLDKsCYTSy9P2myul5JtFDAV9Zkp449R3_QGfcPVP4s8ElUMe509YP-BADAABHq9F-pc7mvpb7gy20lgoMpQZp1CutxshKYqvJaPkZfKfG50cIS_dPvQByjb3ymL7yIYuA5iO7gNR5ek-oMFgOABDsE7pwlpVNXWXxGNh-P4r9Rz1ibHZtA9cgQ-rXwH_QyhjZeBQahaTcq1t8vVEt84lg-y-inLe2d6F1ju0rgAQPACAnrw-CX37k54JvkKMwu1nH6ogJUL-1ltxV1JyPgeh7VBbGmZH09-tvKrH7tyRCXRm0Rni2GNY4qj9EWstX5REB-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c14d192298p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"i\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE\"],\"nt\":\"1\",\"n\":[\"EE79FVtQ1Pnqr-8D3blQ9tYUFwD0iFG6GPUzJY535pBL\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAAB1SMw8YQRwuYupgvCzet2IUh7K9znRKjMFWWh5ggo34AuJU2vsXKBaYLniG-2oUnN0olEWsOn86GkL_hF_lG8K-BADAADT9YbjHaEPeK_Jq9jesuGJa3aHpJ33ThmQrn2IWduagHdCkByllKxttIl00Gjb4oKVAawmQR4FtcRkIkROKKgKABA_flZWgeuqjA35xxBF2in2PvJAllFnVm_scMTxj0Z2XwvnmbR3d4swkbAihbMICO-wR7G_PvTCnMdAfCtqbjILACCbMY4zxDvNmH4M00w3DCi5yR19U84pXk2isPWNFIB-Hg4EZwIp8eiEx9QuIL6RbFIHYRj9oEmqCF12kEx-2xUI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c39d227900p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"ii\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AKX5gPKBrMQwUSdw6EhqISMRbD95plFcW-wwtkqDQ32Q\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\",\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"ri\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"dt\":\"2024-05-02T18:09:53.059000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll{\"v\":\"ACDC10JSON0007fc_\",\"d\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"u\":\"0ACZttAKXurDupF7a0Rzp7Cl\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"ri\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw\",\"a\":{\"d\":\"EDo4VRHRm_GGAg2avTf61dB-JemgPVDY3JK1y4FSIKq2\",\"i\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"u\":\"0ADhoDsBlG-eF8XQ2tkSE2I7\",\"LEI\":\"875500ELOZEL05BVXV37\",\"personLegalName\":\"John Doe\",\"engagementContextRole\":\"EBA Data Submitter\",\"dt\":\"2024-05-02T18:09:53.059000+00:00\"},\"e\":{\"d\":\"EJhEJW-Z1ESS8IwpG2L2lXQo-m_QNmjRPUgU4lD8Nc3F\",\"le\":{\"n\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY\"}},\"r\":{\"d\":\"EIfq_m1DI2IQ1MgHhUl9sq3IQ_PJP9WQ1LhbMscngDCB\",\"usageDisclaimer\":{\"l\":\"Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.\"},\"issuanceDisclaimer\":{\"l\":\"All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.\"},\"privacyDisclaimer\":{\"l\":\"It is the sole responsibility of Holders as Issuees of an ECR vLEI Credential to present that Credential in a privacy-preserving manner using the mechanisms provided in the Issuance and Presentation Exchange (IPEX) protocol specification and the Authentic Chained Data Container (ACDC) specification. https://github.com/WebOfTrust/IETF-IPEX and https://github.com/trustoverip/tswg-acdc-specification.\"}}}-IABEElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk0AAAAAAAAAAAAAAAAAAAAAAAEEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0" -}""""Foo"] + "vlei": [ + """{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DBZxSKiTUaSiWTrkAOAMk_WyCivZUbwMFIJ-vZZHF7D5\"],\"nt\":\"1\",\"n\":[\"ELgz3XamjqxCNWe7rvih9UAzB-1dH7-zBgW8E7RF3OeJ\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABidds3sVtwYGBKl1iyLY9SVpKDk65FoWS48gWUjjhvIcn0Jne-C6ZJd9zi_c6ioNmK1IVkkfTjbqZKVxSgmDgN-BADAAD5CkTmodWH9yR-tcQ_I2l1SJWxFQxpn2fCU4BmgjLt06gNMtpGetiFyINg_t0n-o9VS3ioW8Rs3DXMclrh7HIOABAFKfy1eQ9i6Hk8bORdkW5uwGg3LQrZoUSTnj_2i3TCH45EPTJVnkjObUAYZ1_AQWVPdouVj0Am0PUJPwT1JNsLACCKgFfpBxm8aQQdqAgwp2INz6w9WZ4oJAe_cUxEAqMnnTKzP8eRcj5JNCbzEN8wWNdh5CB5Y6OTlanTxl46--AI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d795649p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"1\",\"p\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"a\":[{\"i\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"s\":\"0\",\"d\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\"}]}-VBq-AABAACXy8MOpF7kY3AxtHTbD__kva6VnLI9ya9Niz6N9vnbWgA5zFc15BPgEFE6kAwonHIix6LXrVKpJB59k0oE_5oE-BADAABp3uFwoL25z46tu1--4oz0hkoAdywAjT6Ojj2IKGk7WJhmCzXPxBsV02546HD5mEqqg6NqWtuv7vyZJgivZZ8EABCvL4-71heQ9S3I8ZaAc-gJnwpSM3oKOpspCas94mBuwrRBu4XWoscMMpKR6j6a9hKlPcdRbkehmAoRRO9HyhcLACAzxTDXn8FegSMumEpg7IE7xipLaPF6qgY1POCVALBTXB7oBUPgf7yr1ooXkCTQ8tEfSm0r5LM18UJOS74KTvcI-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d676775p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EOflFmKcSNvILfVWT6maf56_PhfrYoTy5p5yIkmv4jvs\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"2\",\"p\":\"EAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m\",\"a\":[{\"i\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"0\",\"d\":\"EJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon\"}]}-VBq-AABAADH7raYUgVuo4xNT4P6cuXkik7HkETKGauy0_pJzAR_QGuSROg4PxDLnVjnDxoR3FeTNnav3Sug5FNY_0JH8yUP-BADAAA6hMLoDDixEGSYzK4abi__-24-c0L0G6fdWKZGfiRIlLoMCFVfRicHTn2am4-kaRMZg1O04L3VZ4fIAHgHnu8OABCHAdvvoiATpku1wskGpYR51NVv23N_8KIyeKs4nGDrwkTV3NYFj-m870LBDOXq5oKPYTWEUtZQzB9MGR8p4z0JACDghx2nEpMd8m_vt1wa3y5rbP156DTsxr25ZL2US2M57x5IJoYVqc-CBMUYdYbCa-zZtgXFCs6M6utkE7Er1vMJ-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d710371p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DAfu3nimJGpxVlNmrKUo3c7ZeP-dycDXyPy6U-Lz26jp\"],\"nt\":\"1\",\"n\":[\"EMQw8RvFgkipzpdIaRW02ejSG-05plHrRCglcDrmwD8u\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAADTjXCCg44hkPpfuC-Eu3EDcoxRN9vMnprVBDMfUwFjE3I7AmD07tKqz9F94NjM_ydWi8erDW0vTJM2DHpEYtYL-BADAAC55GJFxVmnRxUGLVC2McbqRA-s7HQqneO7Mnisk7812QlPLj2hy2kPNfyEjp6jeyXV9tGAjGbKWt58WnlmAFEBABARHPH340biYzu2TUohaSjPvXbZMNToGChDSgMKRbt7TpO7PCk46MEFL8BCb4N4v8OAC7Z6ACjL0vqgVncqyo8BACCz6X7GQUD2W7TkW_3V7RCPK8X-IosNsr2mZqu5PNZi7kYWZi2VF-KB_FCjzh3zSAyeFKRVte9o5HbxqM6H3vkI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d757209p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"1\",\"p\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"a\":[{\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"0\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\"}]}-VBq-AABAADcd2ApP_32GJSwJhMj8uU2YPN7KNwoYq8lbV1cRR6sYRJj-Kz5kUMeuXTrrqN4n3SOyML9eAPXPB__WPCvHqAF-BADAAB1pJ0n6mMD577jvKTlsVy3xoJMyK4GlNLKKmZgb8bBo8hHUft3aWDDXaMdOTyZ2YQk3vO4LJVEcznE2uecMGIMABCy0tge4t3DlGNd1s5X1zUsEKQVCt7OYtcsI88Zem7AEP4Pkh6A7pWZ1DIlk3cFdYC--jc1sUWKGmMOP95WSLEPACA4hcWCGyeUSVCfLrfXAR5lmQlB7cSxYwhTId2_Rmxh-KIPLHYpcrvyiXknQZeDTzIdLsc9kvhv8hGim_kl6sEK-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d380030p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"2\",\"p\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"a\":[{\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\"}]}-VBq-AABAABrODkziLBApmEjZsTZhij8_2WzFqXDCcBYp-Vz5tg0vuMOlq0yP-gN6kfhw9UD8x0FryplrPNCDD0eKNClgCUC-BADAACUv_4vyoQJWhm-ruioMY7RLyjFpncjEz6_NDRu48IJVmS6dFJGfRBNp1G_LlegcfsMoKkPx71hvXg1_tfYq9gJABDaQGYRc4jUEtD-k9AUl_vs574fdUi3Bf_N0zxKhEbsjAugUrtQHEVJcT9cB4eUffdPc6bSXis0YQ10Z55mFT8EACBXwhZYi11Tmt8lXvjhXaKDixHWOFkLnbBR2XpSy3we936cyLWFMFG-q0AY_CHaVTdzYJy_OS850GYBwNOsyZYF-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d410786p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"3\",\"p\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"a\":[{\"i\":\"EHX1GUC8HuRzQAF1iVs3-rM1960U99q1q7bKQItPU1gN\",\"s\":\"0\",\"d\":\"EKyOm3y9JM9SNFdLIlBG_VTfjejpKEE2KgEnPKawvuab\"}]}-VBq-AABAABJGMeW1N18v0tvhuYUe27QZL1OvhDRJhVIo7qKMc9gd4NbOzXWjuLPcPZtcvKkvTFb66l6otC41Varj19VRjcM-BADAAADWQ64oLPFhhlZYbONLFYbsQl_nChHdmPmsD9zpYFXbnEB2kzhCrMGdOdtSqW0Nb7Rezogmg969qeDj_9Lo0cMABBEjMp3Fqel1YiCRvfTZ3eJMUt15HRSyC8N-QQFctaEViPctD0U5GDa_n78PaSXPv_-dcHtIk1hp4ORHYGNLyMPACCZUVpA6S4u5xugTO2izUp-5Wit7ngDWZnkmo4XHmjedEVlm1LmoLz_w7fSg9dCxv9ihr5Z02I47jR0tBEcURUE-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c10c04d723024p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EPQYOdd-c0fjHvkGmFqO0fMTBnZBDDnxZ6REUB_QH_no\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"4\",\"p\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"a\":[{\"i\":\"EKXFyihmxElIC7NzFhV2eFXfKiL9iBk1kc5yNU7Hxw13\",\"s\":\"0\",\"d\":\"EFmAdnmvgKAVccDdnMT21jyp7x_hObyAT0ok5YCJjfLj\"}]}-VBq-AABAAD6ti3L4CI7-Nw9oZ9r3U75EFo1J2s6ekJBy08jiLHrFd3z41ZaYpHo4PmVuB1u5oOWbmcQjnqm8Y40hcYIygIL-BADAAB1CEybl44qmmMWJ22rYkCuZ0ww70Ae8OqdqhU-hyReQOEG9VwdajI_HBfwtNbUOyaExvCOfHCMwfXI8dmcaC0HABCi0K4Y7O5KW7YjyERIzQTcoPeu6pbtyC8EXx1JdNCum-CBMtIB2mTihW5tIF1RGFYmaxorIKzfSbIrkGAgtSkJACAtcjAjhqh5DJlWh_MzhcLepx7wgFlA9ZDWngB2uEtLDXVVS41QyXKtBsHoOJntdic1nnXMOoD2r-zHqjzzM-EC-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c13d974476p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"i\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"ii\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AMvLdXXYFhqV93b7mZIXHlqGygDmYryUEz5WR_hoI6go\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEAKGRio-1uLaeUSrC1KycUt3ngE0Q1PAm-IuHB6JLZ5m{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon\",\"i\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"0\",\"ri\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"dt\":\"2024-05-02T18:09:46.265000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACEOflFmKcSNvILfVWT6maf56_PhfrYoTy5p5yIkmv4jvs{\"v\":\"ACDC10JSON000197_\",\"d\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"i\":\"EIykCb_5OR9uBmR-HUWjTAfafYwjMF2OwvLvicb_QV07\",\"ri\":\"ENZzEeJ3FSzbaz-nO0sgC1AAzOrpbwJn741ZzdEShZsW\",\"s\":\"EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao\",\"a\":{\"d\":\"ECw1gKAF2S2z9j9jlMxa0SsMkX-Okr2MxCKCQKMQUz1O\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"LEI\":\"254900OPPU84GM83MG36\",\"dt\":\"2024-05-02T18:09:46.265000+00:00\"}}-IABEKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE0AAAAAAAAAAAAAAAAAAAAAAAEJd5MClDm45CD5pxsJUTxL2WqZOP2T6nP2_GSHKXaFon{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DAfu3nimJGpxVlNmrKUo3c7ZeP-dycDXyPy6U-Lz26jp\"],\"nt\":\"1\",\"n\":[\"EMQw8RvFgkipzpdIaRW02ejSG-05plHrRCglcDrmwD8u\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAADTjXCCg44hkPpfuC-Eu3EDcoxRN9vMnprVBDMfUwFjE3I7AmD07tKqz9F94NjM_ydWi8erDW0vTJM2DHpEYtYL-BADAAC55GJFxVmnRxUGLVC2McbqRA-s7HQqneO7Mnisk7812QlPLj2hy2kPNfyEjp6jeyXV9tGAjGbKWt58WnlmAFEBABARHPH340biYzu2TUohaSjPvXbZMNToGChDSgMKRbt7TpO7PCk46MEFL8BCb4N4v8OAC7Z6ACjL0vqgVncqyo8BACCz6X7GQUD2W7TkW_3V7RCPK8X-IosNsr2mZqu5PNZi7kYWZi2VF-KB_FCjzh3zSAyeFKRVte9o5HbxqM6H3vkI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d757209p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"1\",\"p\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"a\":[{\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"0\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\"}]}-VBq-AABAADcd2ApP_32GJSwJhMj8uU2YPN7KNwoYq8lbV1cRR6sYRJj-Kz5kUMeuXTrrqN4n3SOyML9eAPXPB__WPCvHqAF-BADAAB1pJ0n6mMD577jvKTlsVy3xoJMyK4GlNLKKmZgb8bBo8hHUft3aWDDXaMdOTyZ2YQk3vO4LJVEcznE2uecMGIMABCy0tge4t3DlGNd1s5X1zUsEKQVCt7OYtcsI88Zem7AEP4Pkh6A7pWZ1DIlk3cFdYC--jc1sUWKGmMOP95WSLEPACA4hcWCGyeUSVCfLrfXAR5lmQlB7cSxYwhTId2_Rmxh-KIPLHYpcrvyiXknQZeDTzIdLsc9kvhv8hGim_kl6sEK-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d380030p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"2\",\"p\":\"EHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL\",\"a\":[{\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\"}]}-VBq-AABAABrODkziLBApmEjZsTZhij8_2WzFqXDCcBYp-Vz5tg0vuMOlq0yP-gN6kfhw9UD8x0FryplrPNCDD0eKNClgCUC-BADAACUv_4vyoQJWhm-ruioMY7RLyjFpncjEz6_NDRu48IJVmS6dFJGfRBNp1G_LlegcfsMoKkPx71hvXg1_tfYq9gJABDaQGYRc4jUEtD-k9AUl_vs574fdUi3Bf_N0zxKhEbsjAugUrtQHEVJcT9cB4eUffdPc6bSXis0YQ10Z55mFT8EACBXwhZYi11Tmt8lXvjhXaKDixHWOFkLnbBR2XpSy3we936cyLWFMFG-q0AY_CHaVTdzYJy_OS850GYBwNOsyZYF-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d410786p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"3\",\"p\":\"EB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros\",\"a\":[{\"i\":\"EHX1GUC8HuRzQAF1iVs3-rM1960U99q1q7bKQItPU1gN\",\"s\":\"0\",\"d\":\"EKyOm3y9JM9SNFdLIlBG_VTfjejpKEE2KgEnPKawvuab\"}]}-VBq-AABAABJGMeW1N18v0tvhuYUe27QZL1OvhDRJhVIo7qKMc9gd4NbOzXWjuLPcPZtcvKkvTFb66l6otC41Varj19VRjcM-BADAAADWQ64oLPFhhlZYbONLFYbsQl_nChHdmPmsD9zpYFXbnEB2kzhCrMGdOdtSqW0Nb7Rezogmg969qeDj_9Lo0cMABBEjMp3Fqel1YiCRvfTZ3eJMUt15HRSyC8N-QQFctaEViPctD0U5GDa_n78PaSXPv_-dcHtIk1hp4ORHYGNLyMPACCZUVpA6S4u5xugTO2izUp-5Wit7ngDWZnkmo4XHmjedEVlm1LmoLz_w7fSg9dCxv9ihr5Z02I47jR0tBEcURUE-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c10c04d723024p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EPQYOdd-c0fjHvkGmFqO0fMTBnZBDDnxZ6REUB_QH_no\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"4\",\"p\":\"EFA1bxl24mDxMq42jUMhfZda0ZnFyl0y77UAs-BkCi_e\",\"a\":[{\"i\":\"EKXFyihmxElIC7NzFhV2eFXfKiL9iBk1kc5yNU7Hxw13\",\"s\":\"0\",\"d\":\"EFmAdnmvgKAVccDdnMT21jyp7x_hObyAT0ok5YCJjfLj\"}]}-VBq-AABAAD6ti3L4CI7-Nw9oZ9r3U75EFo1J2s6ekJBy08jiLHrFd3z41ZaYpHo4PmVuB1u5oOWbmcQjnqm8Y40hcYIygIL-BADAAB1CEybl44qmmMWJ22rYkCuZ0ww70Ae8OqdqhU-hyReQOEG9VwdajI_HBfwtNbUOyaExvCOfHCMwfXI8dmcaC0HABCi0K4Y7O5KW7YjyERIzQTcoPeu6pbtyC8EXx1JdNCum-CBMtIB2mTihW5tIF1RGFYmaxorIKzfSbIrkGAgtSkJACAtcjAjhqh5DJlWh_MzhcLepx7wgFlA9ZDWngB2uEtLDXVVS41QyXKtBsHoOJntdic1nnXMOoD2r-zHqjzzM-EC-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c13d974476p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DJK74-FQwLUyYcopt5R5soei4Eqdoq0ucXSqC6ynYkvA\"],\"nt\":\"1\",\"n\":[\"EFpMnM4SPlishuuMS-fonwSD0LLBmTnIdgQjG5LuXW30\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABSYMBKbhVWAC1rYAIgf800CJ7Oylr9MvGVtKgoYcquCe94w6gjuTSzTqfrbjQqy-PN4TPxVtI5rnWQJR70kj4E-BADAADJJcHNEe41Thz2MU5WXj78pcto-C0lJVtC34T4rrbH2jf7i2zRFzsbHY8jLcnzdoINTm5wJunE_FWdFaTJQ9QNABCdkgIlJM0h9_EesHn1yzSBnaTch4959sUrKanXjAZ-qiXkjQ3lpg9UPyNweo9GIRN92-KIFv6xBCYIKFn0p5wLACDMKP_62CSxJ9KyNbaPAjoPGwiIUNFD5Mwu6tmUYYMrKibYqjvIrA5s_YXUFPOA3tYhsZqmm2kWOUTPLlpTqTUC-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d734341p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"1\",\"p\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"a\":[{\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"0\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\"}]}-VBq-AABAADVkgj4VZWQyrRzbUFm4vwjqhf4V-czR-HYlFLVRqXszQ4vFq4bP8UaWNFBHtMjUEbJKHvWKvF5H8236uswdgkM-BADAACJ0pvCwXBEsxG5MkPeHRGMmojxfTOS-JZQ8Qz8CSgptAUd3tbelPT0jIQNeQmqAsXxvVIXDVIqmbeDZYMfQwAIABCRs9myd_SbYLTAdBhu6cmnpp9K5bLX0vMrrmxwY5G_iDO3Dvigg2VFLTdZ6VPnVMl-B7bDcv6iWKFHZWF_0X8PACCsGXF4DmCVFBJal4mUPk9t4HPAFyB9miv1mcg73Bki-T4SfoDJ4A2nifZU4O5KtotYCmSdznamY0BsYkD2LNoD-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d224186p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"2\",\"p\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"a\":[{\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\"}]}-VBq-AABAAAllpN9qv5xOx9hE4SDS1VanWzqB8CD_k1y-vvlH487CoTTrPTm8Di1OICl0zpWBGh9LJqDCIWAnlG_cO992hYI-BADAAC7cIVpeaE7xsOhyEG3lGrqHw6rXQsQdhFaWNujGnLSTBI23ZyhsZ8bDdFwZGNb8woy9_arIwZlhOXdIPxOXvwOABCri66kCiEkExor7l1JQz-kR2bHgx3UENtDr_GiuZ3xc7hbQPo0dhXCbjtuw_enEdYzb5IsKKu0On8lmnAvAEcGACBYBDs8TZTPWFFlvoyiVf5oOUExGPFb6v93BnFitMMoGPmixEvqsRedWxuMmxKD7Bq1dqGFKE6G9kNORQ7Kx-8F-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d256392p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"3\",\"p\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"a\":[{\"i\":\"EAwFSM6lx6AE9XG-r1_PBQi0aVhZms3uQ4svV-Ao0k0u\",\"s\":\"0\",\"d\":\"EAFlIYst1qwaYxkCdaQi8X-HwZYn1KF4P-8lWyIE00Pr\"}]}-VBq-AABAAAC5KZgiI38wZ7KEaAY-M6nG5PSpasuv7Dc0PKl8_ZhAaa7o8xJY7oOSzTE8IbRjUwJ1riLmOG5s-dvrwDGt5QA-BADAACnA2pacEht173RaEX7XkPuizhcL0ZVnz28UAF0eWa97Yv6h_B1wFFuR8b-EqJhZKBkp6twrf5wEsfnS4_QJbsDABDD-x6OxRuBgCx_1EBJ0fuCyQse0S_RViHm78lcF9wwnMOer4puQfbI-PXcRyYF9w8dX1xMTzX0q21OiTYfvggIACBA3OSnnD_2_IoJXeLvk_blg3-PSihOf-rv6HEwzKYql-cYCcRgFUivZiM8bTwxokme-QNaV3cY2_c-tShUuzgC-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c09c59d325680p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EJqQeIjHV-uPfCUCgLBeJI8k85u8GKl35IeaR4YZ6RIc\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"4\",\"p\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"a\":[{\"i\":\"EF8wfC9k3VQPIUcpTbKLWyC7nXRcztDArGMYdfBntW-h\",\"s\":\"0\",\"d\":\"EG9GFCb1q2P0PJ42TK9BbjOes8FCZgpHguAfMxkdrtZi\"}]}-VBq-AABAABOqvKRb57P_j19Vl0HVHfDUbdLWPz7yMRLDKsCYTSy9P2myul5JtFDAV9Zkp449R3_QGfcPVP4s8ElUMe509YP-BADAABHq9F-pc7mvpb7gy20lgoMpQZp1CutxshKYqvJaPkZfKfG50cIS_dPvQByjb3ymL7yIYuA5iO7gNR5ek-oMFgOABDsE7pwlpVNXWXxGNh-P4r9Rz1ibHZtA9cgQ-rXwH_QyhjZeBQahaTcq1t8vVEt84lg-y-inLe2d6F1ju0rgAQPACAnrw-CX37k54JvkKMwu1nH6ogJUL-1ltxV1JyPgeh7VBbGmZH09-tvKrH7tyRCXRm0Rni2GNY4qj9EWstX5REB-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c14d192298p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"i\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"ii\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AE5fEuU0vYME2TacvBnNQ2mMHaurTSwXNKvttHOKfOiK\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEHi48Sz541kMh1ZOAwoNAhZ5lBXxp4xJVA9oQND2F9kL{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y\",\"i\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"0\",\"ri\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"dt\":\"2024-05-02T18:09:48.374000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACEB3aIgaUVwGL9Epiw8tZYGBzknwhrlwHD8ThMHLDRros{\"v\":\"ACDC10JSON0005c8_\",\"d\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"i\":\"EHK-jDUv8JqFvh8sh7I1aWklvYtHPZLaJv3asD0bEu5R\",\"ri\":\"EMd6_IRhfKhEsN0GqR11VODmkj6alcGKlJjm5xiQVkxu\",\"s\":\"ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY\",\"a\":{\"d\":\"ENIkuwNRRNS2pq_B8H32qCnZDB51VA9JG0MFa7yjZUgw\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"LEI\":\"875500ELOZEL05BVXV37\",\"dt\":\"2024-05-02T18:09:48.374000+00:00\"},\"e\":{\"d\":\"EJuWUqd2JBMzzXItL-FW5T58N86_NC1049syzsXrnVgF\",\"qvi\":{\"n\":\"EKH4QNoKz94220r88MQ3I2M7wziOx3nTrLyFx66acPGE\",\"s\":\"EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao\"}},\"r\":{\"d\":\"EGZ97EjPSINR-O-KHDN_uw4fdrTxeuRXrqT5ZHHQJujQ\",\"usageDisclaimer\":{\"l\":\"Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.\"},\"issuanceDisclaimer\":{\"l\":\"All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.\"}}}-IABEDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI0AAAAAAAAAAAAAAAAAAAAAAAEDNcjBXZjc06sL7m7uiM8zKc9QglxCU8jSt3qzfdZI1Y{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DJK74-FQwLUyYcopt5R5soei4Eqdoq0ucXSqC6ynYkvA\"],\"nt\":\"1\",\"n\":[\"EFpMnM4SPlishuuMS-fonwSD0LLBmTnIdgQjG5LuXW30\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAABSYMBKbhVWAC1rYAIgf800CJ7Oylr9MvGVtKgoYcquCe94w6gjuTSzTqfrbjQqy-PN4TPxVtI5rnWQJR70kj4E-BADAADJJcHNEe41Thz2MU5WXj78pcto-C0lJVtC34T4rrbH2jf7i2zRFzsbHY8jLcnzdoINTm5wJunE_FWdFaTJQ9QNABCdkgIlJM0h9_EesHn1yzSBnaTch4959sUrKanXjAZ-qiXkjQ3lpg9UPyNweo9GIRN92-KIFv6xBCYIKFn0p5wLACDMKP_62CSxJ9KyNbaPAjoPGwiIUNFD5Mwu6tmUYYMrKibYqjvIrA5s_YXUFPOA3tYhsZqmm2kWOUTPLlpTqTUC-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c41d734341p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"1\",\"p\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"a\":[{\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"0\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\"}]}-VBq-AABAADVkgj4VZWQyrRzbUFm4vwjqhf4V-czR-HYlFLVRqXszQ4vFq4bP8UaWNFBHtMjUEbJKHvWKvF5H8236uswdgkM-BADAACJ0pvCwXBEsxG5MkPeHRGMmojxfTOS-JZQ8Qz8CSgptAUd3tbelPT0jIQNeQmqAsXxvVIXDVIqmbeDZYMfQwAIABCRs9myd_SbYLTAdBhu6cmnpp9K5bLX0vMrrmxwY5G_iDO3Dvigg2VFLTdZ6VPnVMl-B7bDcv6iWKFHZWF_0X8PACCsGXF4DmCVFBJal4mUPk9t4HPAFyB9miv1mcg73Bki-T4SfoDJ4A2nifZU4O5KtotYCmSdznamY0BsYkD2LNoD-EAB0AAAAAAAAAAAAAAAAAAAAAAB1AAG2024-05-02T18c09c56d224186p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"2\",\"p\":\"EBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q\",\"a\":[{\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\"}]}-VBq-AABAAAllpN9qv5xOx9hE4SDS1VanWzqB8CD_k1y-vvlH487CoTTrPTm8Di1OICl0zpWBGh9LJqDCIWAnlG_cO992hYI-BADAAC7cIVpeaE7xsOhyEG3lGrqHw6rXQsQdhFaWNujGnLSTBI23ZyhsZ8bDdFwZGNb8woy9_arIwZlhOXdIPxOXvwOABCri66kCiEkExor7l1JQz-kR2bHgx3UENtDr_GiuZ3xc7hbQPo0dhXCbjtuw_enEdYzb5IsKKu0On8lmnAvAEcGACBYBDs8TZTPWFFlvoyiVf5oOUExGPFb6v93BnFitMMoGPmixEvqsRedWxuMmxKD7Bq1dqGFKE6G9kNORQ7Kx-8F-EAB0AAAAAAAAAAAAAAAAAAAAAAC1AAG2024-05-02T18c09c56d256392p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"3\",\"p\":\"ENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll\",\"a\":[{\"i\":\"EAwFSM6lx6AE9XG-r1_PBQi0aVhZms3uQ4svV-Ao0k0u\",\"s\":\"0\",\"d\":\"EAFlIYst1qwaYxkCdaQi8X-HwZYn1KF4P-8lWyIE00Pr\"}]}-VBq-AABAAAC5KZgiI38wZ7KEaAY-M6nG5PSpasuv7Dc0PKl8_ZhAaa7o8xJY7oOSzTE8IbRjUwJ1riLmOG5s-dvrwDGt5QA-BADAACnA2pacEht173RaEX7XkPuizhcL0ZVnz28UAF0eWa97Yv6h_B1wFFuR8b-EqJhZKBkp6twrf5wEsfnS4_QJbsDABDD-x6OxRuBgCx_1EBJ0fuCyQse0S_RViHm78lcF9wwnMOer4puQfbI-PXcRyYF9w8dX1xMTzX0q21OiTYfvggIACBA3OSnnD_2_IoJXeLvk_blg3-PSihOf-rv6HEwzKYql-cYCcRgFUivZiM8bTwxokme-QNaV3cY2_c-tShUuzgC-EAB0AAAAAAAAAAAAAAAAAAAAAAD1AAG2024-05-02T18c09c59d325680p00c00{\"v\":\"KERI10JSON00013a_\",\"t\":\"ixn\",\"d\":\"EJqQeIjHV-uPfCUCgLBeJI8k85u8GKl35IeaR4YZ6RIc\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"4\",\"p\":\"ED-awJsyw2rA6zQzALApHCh9IrndYPpGmWtzT_Me9ZbZ\",\"a\":[{\"i\":\"EF8wfC9k3VQPIUcpTbKLWyC7nXRcztDArGMYdfBntW-h\",\"s\":\"0\",\"d\":\"EG9GFCb1q2P0PJ42TK9BbjOes8FCZgpHguAfMxkdrtZi\"}]}-VBq-AABAABOqvKRb57P_j19Vl0HVHfDUbdLWPz7yMRLDKsCYTSy9P2myul5JtFDAV9Zkp449R3_QGfcPVP4s8ElUMe509YP-BADAABHq9F-pc7mvpb7gy20lgoMpQZp1CutxshKYqvJaPkZfKfG50cIS_dPvQByjb3ymL7yIYuA5iO7gNR5ek-oMFgOABDsE7pwlpVNXWXxGNh-P4r9Rz1ibHZtA9cgQ-rXwH_QyhjZeBQahaTcq1t8vVEt84lg-y-inLe2d6F1ju0rgAQPACAnrw-CX37k54JvkKMwu1nH6ogJUL-1ltxV1JyPgeh7VBbGmZH09-tvKrH7tyRCXRm0Rni2GNY4qj9EWstX5REB-EAB0AAAAAAAAAAAAAAAAAAAAAAE1AAG2024-05-02T18c10c14d192298p00c00{\"v\":\"KERI10JSON0001b7_\",\"t\":\"icp\",\"d\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"i\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE\"],\"nt\":\"1\",\"n\":[\"EE79FVtQ1Pnqr-8D3blQ9tYUFwD0iFG6GPUzJY535pBL\"],\"bt\":\"3\",\"b\":[\"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha\",\"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM\",\"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX\"],\"c\":[],\"a\":[]}-VBq-AABAAB1SMw8YQRwuYupgvCzet2IUh7K9znRKjMFWWh5ggo34AuJU2vsXKBaYLniG-2oUnN0olEWsOn86GkL_hF_lG8K-BADAADT9YbjHaEPeK_Jq9jesuGJa3aHpJ33ThmQrn2IWduagHdCkByllKxttIl00Gjb4oKVAawmQR4FtcRkIkROKKgKABA_flZWgeuqjA35xxBF2in2PvJAllFnVm_scMTxj0Z2XwvnmbR3d4swkbAihbMICO-wR7G_PvTCnMdAfCtqbjILACCbMY4zxDvNmH4M00w3DCi5yR19U84pXk2isPWNFIB-Hg4EZwIp8eiEx9QuIL6RbFIHYRj9oEmqCF12kEx-2xUI-EAB0AAAAAAAAAAAAAAAAAAAAAAA1AAG2024-05-02T18c09c39d227900p00c00{\"v\":\"KERI10JSON000113_\",\"t\":\"vcp\",\"d\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"i\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"ii\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"s\":\"0\",\"c\":[\"NB\"],\"bt\":\"0\",\"b\":[],\"n\":\"AKX5gPKBrMQwUSdw6EhqISMRbD95plFcW-wwtkqDQ32Q\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABEBuuACjEQdbJ4lcH5DGQubWDUchRcS8EydwUwDZW0D7Q{\"v\":\"KERI10JSON0000ed_\",\"t\":\"iss\",\"d\":\"EEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0\",\"i\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"s\":\"0\",\"ri\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"dt\":\"2024-05-02T18:09:53.059000+00:00\"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAACENg8Yq0qb4eAKEMwpqfc44KlhGGVkBbuKeah8GG0zyll{\"v\":\"ACDC10JSON0007fc_\",\"d\":\"EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk\",\"u\":\"0ACZttAKXurDupF7a0Rzp7Cl\",\"i\":\"EBe70eY4J_CEgARnE9g6a73w7FPChZQN-FzDlZ-w9FHk\",\"ri\":\"ELil0P1CmDcBIngMUYw4cm7k6DzaWGRI5gUW9mzrnsPZ\",\"s\":\"EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw\",\"a\":{\"d\":\"EDo4VRHRm_GGAg2avTf61dB-JemgPVDY3JK1y4FSIKq2\",\"i\":\"EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\",\"u\":\"0ADhoDsBlG-eF8XQ2tkSE2I7\",\"LEI\":\"875500ELOZEL05BVXV37\",\"personLegalName\":\"John Doe\",\"engagementContextRole\":\"EBA Data Submitter\",\"dt\":\"2024-05-02T18:09:53.059000+00:00\"},\"e\":{\"d\":\"EJhEJW-Z1ESS8IwpG2L2lXQo-m_QNmjRPUgU4lD8Nc3F\",\"le\":{\"n\":\"EDqh-8Lobj56DJP7g7O-vZ3qBL2mTyXhFq2rDQC-86mI\",\"s\":\"ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY\"}},\"r\":{\"d\":\"EIfq_m1DI2IQ1MgHhUl9sq3IQ_PJP9WQ1LhbMscngDCB\",\"usageDisclaimer\":{\"l\":\"Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.\"},\"issuanceDisclaimer\":{\"l\":\"All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.\"},\"privacyDisclaimer\":{\"l\":\"It is the sole responsibility of Holders as Issuees of an ECR vLEI Credential to present that Credential in a privacy-preserving manner using the mechanisms provided in the Issuance and Presentation Exchange (IPEX) protocol specification and the Authentic Chained Data Container (ACDC) specification. https://github.com/WebOfTrust/IETF-IPEX and https://github.com/trustoverip/tswg-acdc-specification.\"}}}-IABEElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk0AAAAAAAAAAAAAAAAAAAAAAAEEE7jq1J_yV7j6epqdks2s4iKi7olmRXHmMqN9Symeo0" +}""" + "Foo" + ], }, "response": { "aid": ["EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x"], - "said": ["EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk"] - } + "said": ["EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk"], + }, } check_login_examples = { - "request": { - "aid": "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x" - }, + "request": {"aid": "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x"}, "response": { "aid": ["EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x"], - "said": ["EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk"] - } + "said": ["EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk"], + }, } upload_examples = { @@ -25,13 +26,11 @@ "aid": "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x", "dig": "EC7b6S50sY26HTj6AtQiWMDMucsBxMvThkmrKUBXVMf0", "headers": { - "signature": - 'indexed="?0";signify="0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF"', - "signature_input": - 'signify=("@method" "@path" "signify-resource" "signify-timestamp");created=1714854033;keyid="BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE";alg="ed25519"', + "signature": 'indexed="?0";signify="0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF"', + "signature_input": 'signify=("@method" "@path" "signify-resource" "signify-timestamp");created=1714854033;keyid="BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE";alg="ed25519"', "signify_resource": "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x", - "signify_timestamp": "2023-07-27T13:00:14.802000+00:00" - } + "signify_timestamp": "2023-07-27T13:00:14.802000+00:00", + }, }, "response": { "submitter": ["EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk"], @@ -40,8 +39,9 @@ "contentType": ["application/zip"], "size": [4467], "message": [ - "All 6 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk)."] - } + "All 6 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk)." + ], + }, } check_upload_examples = { @@ -49,19 +49,17 @@ "aid": "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x", "dig": "EC7b6S50sY26HTj6AtQiWMDMucsBxMvThkmrKUBXVMf0", "headers": { - "signature": - "indexed='?0';signify='0BCLs_wv3X6YFoFhB7acH_BePXS7zjBJPvuChdr01cM60Igf_sxYsah9sLHP-pMSYFs1Y6zYUo58HVG8tRd4X1IC'", - "signature_input": - "signify=('@method' '@path' 'signify-resource' 'signify-timestamp');created=1690462814;keyid='BPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9';alg='ed25519'", + "signature": "indexed='?0';signify='0BCLs_wv3X6YFoFhB7acH_BePXS7zjBJPvuChdr01cM60Igf_sxYsah9sLHP-pMSYFs1Y6zYUo58HVG8tRd4X1IC'", + "signature_input": "signify=('@method' '@path' 'signify-resource' 'signify-timestamp');created=1690462814;keyid='BPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9';alg='ed25519'", "signify_resource": "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk", - "signify_timestamp": "2023-07-27T13:00:14.802000+00:00" - } + "signify_timestamp": "2023-07-27T13:00:14.802000+00:00", + }, }, "response": { "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk": [ - "{\"submitter\": \"EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk\", \"filename\": \"test_MetaInfReportJson_noSigs.zip\", \"status\": \"failed\", \"contentType\": \"application/zip\", \"size\": 3059, \"message\": \"5 files from report package not signed {'parameters.csv', 'FilingIndicators.csv', 'report.json', 'i_10.01.csv', 'i_10.02.csv'}, []\"}", - "{\"submitter\": \"EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk\", \"filename\": \"test_ifclass3.zip\", \"status\": \"verified\", \"contentType\": \"application/zip\", \"size\": 5662, \"message\": \"All 9 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk).\"}", - "{\"submitter\": \"EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk\", \"filename\": \"test_ifgroup2023.zip\", \"status\": \"verified\", \"contentType\": \"application/zip\", \"size\": 4467, \"message\": \"All 6 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk).\"}" + '{"submitter": "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk", "filename": "test_MetaInfReportJson_noSigs.zip", "status": "failed", "contentType": "application/zip", "size": 3059, "message": "5 files from report package not signed {\'parameters.csv\', \'FilingIndicators.csv\', \'report.json\', \'i_10.01.csv\', \'i_10.02.csv\'}, []"}', + '{"submitter": "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk", "filename": "test_ifclass3.zip", "status": "verified", "contentType": "application/zip", "size": 5662, "message": "All 9 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk)."}', + '{"submitter": "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk", "filename": "test_ifgroup2023.zip", "status": "verified", "contentType": "application/zip", "size": 4467, "message": "All 6 files in report package have been signed by submitter (EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk)."}', ] - } + }, } diff --git a/src/regps/app/cli/commands/start.py b/src/regps/app/cli/commands/start.py index 3e8aa50..c7e9586 100644 --- a/src/regps/app/cli/commands/start.py +++ b/src/regps/app/cli/commands/start.py @@ -5,6 +5,7 @@ regulation portal servicecommand line interface """ + import argparse # from regps.app import service @@ -12,12 +13,18 @@ d = "Runs regulation portal service\n" d += "\tExample:\nregps\n" parser = argparse.ArgumentParser(description=d) -parser.set_defaults(handler=lambda args: launch(args)) -parser.add_argument('-V', '--version', - action='version', - version="0.0.1", - help="Prints out version of script runner.") -parser.add_argument('-p', '--port', - action='store', - default=4902, - help="Local port number the HTTP server listens on. Default is 4902.") +# parser.set_defaults(handler=lambda args: launch(args)) +parser.add_argument( + "-V", + "--version", + action="version", + version="0.0.1", + help="Prints out version of script runner.", +) +parser.add_argument( + "-p", + "--port", + action="store", + default=4902, + help="Local port number the HTTP server listens on. Default is 4902.", +) diff --git a/src/regps/app/cli/regps.py b/src/regps/app/cli/regps.py index 2e1a148..ae8e3fc 100644 --- a/src/regps/app/cli/regps.py +++ b/src/regps/app/cli/regps.py @@ -4,6 +4,7 @@ regps.app.cli.commands module """ + import logging import multicommand import regps.app.fastapi_app as fastapi_app @@ -14,19 +15,23 @@ def main(): parser = multicommand.create_parser(commands) args = parser.parse_args() - if not hasattr(args, 'handler'): + if not hasattr(args, "handler"): parser.print_help() return try: - logging.info("******* Starting regulation portal service for %s listening: http/%s " - ".******", args.http) + logging.info( + "******* Starting regulation portal service for %s listening: http/%s " + ".******", + args.http, + ) fastapi_app.main() - logging.info("******* Ended reg portal service %s listening: http/%s" - ".******", args.http) - + logging.info( + "******* Ended reg portal service %s listening: http/%s" ".******", + args.http, + ) except Exception as ex: logging.error(f"ERR: {ex}") diff --git a/src/regps/app/fastapi_app.py b/src/regps/app/fastapi_app.py index 3ba3fe1..620dfe4 100644 --- a/src/regps/app/fastapi_app.py +++ b/src/regps/app/fastapi_app.py @@ -1,22 +1,39 @@ import os from collections import defaultdict -from typing import Annotated, Union -from starlette.middleware.base import BaseHTTPMiddleware from regps.app.api.signed_headers_verifier import logger, VerifySignedHeaders -from fastapi import FastAPI, Header, HTTPException, Request, File, UploadFile, Path, Response, Body, Depends, Form +from fastapi import ( + FastAPI, + Header, + HTTPException, + Request, + Path, + Response, +) from fastapi.responses import JSONResponse from starlette.middleware.cors import CORSMiddleware -from regps.app.api.utils.pydantic_models import LoginRequest, LoginResponse, CheckLoginResponse, CheckUploadResponse, \ - UploadResponse -from regps.app.api.exceptions import VerifierServiceException, VerifySignedHeadersException, \ - DigestVerificationFailedException +from regps.app.api.utils.pydantic_models import ( + LoginRequest, + LoginResponse, + CheckLoginResponse, + UploadResponse, +) +from regps.app.api.exceptions import ( + VerifierServiceException, +) from regps.app.api.controllers import APIController -from regps.app.api.utils.swagger_examples import login_examples, check_login_examples, upload_examples, \ - check_upload_examples +from regps.app.api.utils.swagger_examples import ( + check_login_examples, + upload_examples, + check_upload_examples, +) -app = FastAPI(title="Regulator portal service api", description="Regulator web portal service api", version="1.0.0") +app = FastAPI( + title="Regulator portal service api", + description="Regulator web portal service api", + version="1.0.0", +) api_controller = APIController() verify_signed_headers = VerifySignedHeaders(api_controller) @@ -50,14 +67,19 @@ async def login(response: Response, data: LoginRequest): @app.get("/checklogin/{aid}", response_model=CheckLoginResponse) -async def check_login_route(response: Response, - aid: str = Path(..., description="AID", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": check_login_examples["request"]["aid"], - } - }), ): +async def check_login_route( + response: Response, + aid: str = Path( + ..., + description="AID", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": check_login_examples["request"]["aid"], + } + }, + ), +): """ Given an AID returns information about the login """ @@ -76,46 +98,62 @@ async def check_login_route(response: Response, # TODO: Add upload form-data param to the required parameters and add it to the DOC @app.post("/upload/{aid}/{dig}", response_model=UploadResponse) -async def upload_route(request: Request, response: Response, - aid: str = Path(..., description="AID", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": upload_examples["request"]["aid"], - } - }), - dig: str = Path(..., description="DIG", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": upload_examples["request"]["dig"], - } - }), - signature: str = Header(openapi_examples={ - "default": { - "summary": "Default signature", - "value": upload_examples["request"]["headers"]["signature"], - } - }), - signature_input: str = Header(openapi_examples={ - "default": { - "summary": "Default signature_input", - "value": upload_examples["request"]["headers"]["signature_input"], - } - }), - signify_resource: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_resource", - "value": upload_examples["request"]["headers"]["signify_resource"], - } - }), - signify_timestamp: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_timestamp", - "value": upload_examples["request"]["headers"]["signify_timestamp"], - } - }) - ): +async def upload_route( + request: Request, + response: Response, + aid: str = Path( + ..., + description="AID", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": upload_examples["request"]["aid"], + } + }, + ), + dig: str = Path( + ..., + description="DIG", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": upload_examples["request"]["dig"], + } + }, + ), + signature: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature", + "value": upload_examples["request"]["headers"]["signature"], + } + } + ), + signature_input: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature_input", + "value": upload_examples["request"]["headers"]["signature_input"], + } + } + ), + signify_resource: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_resource", + "value": upload_examples["request"]["headers"]["signify_resource"], + } + } + ), + signify_timestamp: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_timestamp", + "value": upload_examples["request"]["headers"]["signify_timestamp"], + } + } + ), +): """ Given an AID and DIG, returns information about the upload """ @@ -128,12 +166,16 @@ async def upload_route(request: Request, response: Response, logger.info( f"Upload: request for {aid} {dig} {raw} {request.headers.get('Content-Type')}" ) - resp = api_controller.upload(aid, dig, report, request.headers.get('Content-Type'), raw) + resp = api_controller.upload( + aid, dig, report, request.headers.get("Content-Type"), raw + ) if resp.status_code >= 400: - logger.info(f"Upload: Invalid signature on report or error was received") + logger.info("Upload: Invalid signature on report or error was received") else: - logger.info(f"Upload: completed upload for {aid} {dig} with code {resp.status_code}") + logger.info( + f"Upload: completed upload for {aid} {dig} with code {resp.status_code}" + ) reports[aid].append(resp.json()) return JSONResponse(status_code=200, content=resp.json()) except HTTPException as e: @@ -146,46 +188,62 @@ async def upload_route(request: Request, response: Response, @app.get("/upload/{aid}/{dig}") -async def check_upload_route(request: Request, response: Response, - aid: str = Path(..., description="AID", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": check_upload_examples["request"]["aid"], - } - }), - dig: str = Path(..., description="DIG", - openapi_examples={ - "default": { - "summary": "The file digest", - "value": check_upload_examples["request"]["dig"], - } - }), - signature: str = Header(openapi_examples={ - "default": { - "summary": "Default signature", - "value": upload_examples["request"]["headers"]["signature"], - } - }), - signature_input: str = Header(openapi_examples={ - "default": { - "summary": "Default signature_input", - "value": upload_examples["request"]["headers"]["signature_input"], - } - }), - signify_resource: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_resource", - "value": upload_examples["request"]["headers"]["signify_resource"], - } - }), - signify_timestamp: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_timestamp", - "value": upload_examples["request"]["headers"]["signify_timestamp"], - } - }) - ): +async def check_upload_route( + request: Request, + response: Response, + aid: str = Path( + ..., + description="AID", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": check_upload_examples["request"]["aid"], + } + }, + ), + dig: str = Path( + ..., + description="DIG", + openapi_examples={ + "default": { + "summary": "The file digest", + "value": check_upload_examples["request"]["dig"], + } + }, + ), + signature: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature", + "value": upload_examples["request"]["headers"]["signature"], + } + } + ), + signature_input: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature_input", + "value": upload_examples["request"]["headers"]["signature_input"], + } + } + ), + signify_resource: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_resource", + "value": upload_examples["request"]["headers"]["signify_resource"], + } + } + ), + signify_timestamp: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_timestamp", + "value": upload_examples["request"]["headers"]["signify_timestamp"], + } + } + ), +): """ Check upload status by aid and dig. """ @@ -203,40 +261,52 @@ async def check_upload_route(request: Request, response: Response, @app.get("/status/{aid}") -async def status_route(request: Request, response: Response, - aid: str = Path(..., description="AID", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": check_upload_examples["request"]["aid"], - } - }), - signature: str = Header(openapi_examples={ - "default": { - "summary": "Default signature", - "value": upload_examples["request"]["headers"]["signature"], - } - }), - signature_input: str = Header(openapi_examples={ - "default": { - "summary": "Default signature_input", - "value": upload_examples["request"]["headers"]["signature_input"], - } - }), - signify_resource: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_resource", - "value": upload_examples["request"]["headers"]["signify_resource"], - } - }), - signify_timestamp: str = Header(openapi_examples={ - "default": { - "summary": "Default signify_timestamp", - "value": upload_examples["request"]["headers"]["signify_timestamp"], - } - }) - - ): +async def status_route( + request: Request, + response: Response, + aid: str = Path( + ..., + description="AID", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": check_upload_examples["request"]["aid"], + } + }, + ), + signature: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature", + "value": upload_examples["request"]["headers"]["signature"], + } + } + ), + signature_input: str = Header( + openapi_examples={ + "default": { + "summary": "Default signature_input", + "value": upload_examples["request"]["headers"]["signature_input"], + } + } + ), + signify_resource: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_resource", + "value": upload_examples["request"]["headers"]["signify_resource"], + } + } + ), + signify_timestamp: str = Header( + openapi_examples={ + "default": { + "summary": "Default signify_timestamp", + "value": upload_examples["request"]["headers"]["signify_timestamp"], + } + } + ), +): """ Check upload status by aid. """ @@ -255,15 +325,20 @@ async def status_route(request: Request, response: Response, # TODO: Remove this endpoint when we will have DB. IT's only for tests @app.post("/status/{aid}/drop") -def clear_status_route(aid: str = Path(..., description="AID", - openapi_examples={ - "default": { - "summary": "Default AID", - "value": check_upload_examples["request"]["aid"], - } - }), ): +def clear_status_route( + aid: str = Path( + ..., + description="AID", + openapi_examples={ + "default": { + "summary": "Default AID", + "value": check_upload_examples["request"]["aid"], + } + }, + ), +): """ - Drop upload status for specified AID. For the test purposes + Drop upload status for specified AID. For the test purposes """ reports[aid] = [] resp = {"status": "success", "aid": aid} @@ -279,15 +354,21 @@ def clear_status_route(aid: str = Path(..., description="AID", allow_methods=["*"], allow_headers=["*"], expose_headers=[ - "cesr-attachment", "cesr-date", "content-type", "signature", "signature-input", - "signify-resource", "signify-timestamp" - ] + "cesr-attachment", + "cesr-date", + "content-type", + "signature", + "signature-input", + "signify-resource", + "signify-timestamp", + ], ) def main(): logger.info("Starting RegPS...") import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/tests/integration/test_service_integration.py b/tests/integration/test_service_integration.py index 92525de..be7d47e 100644 --- a/tests/integration/test_service_integration.py +++ b/tests/integration/test_service_integration.py @@ -68,15 +68,15 @@ def test_ends_integration(start_gunicorn): app = fastapi_app.app client = TestClient(app) - result = client.get(f"/ping", headers=headers) + result = client.get("/ping", headers=headers) assert result.status_code == 200 assert result.text == "Pong" - with open(f"../../data/credential.cesr", "r") as cfile: + with open("../../data/credential.cesr", "r") as cfile: vlei_ecr = cfile.read() headers["Content-Type"] = "application/json+cesr" result = client.post( - f"/login", json={"said": SAID, "vlei": vlei_ecr}, headers=headers + "/login", json={"said": SAID, "vlei": vlei_ecr}, headers=headers ) assert result.status_code == 202 diff --git a/tests/unit/test_login.py b/tests/unit/test_login.py index 9e72979..a7c30df 100644 --- a/tests/unit/test_login.py +++ b/tests/unit/test_login.py @@ -9,7 +9,7 @@ def test_login(): # AID and SAID should be the same as what is in credential.cesr for the ECR credential # see https://trustoverip.github.io/tswg-acdc-specification/#top-level-fields to understand the fields/values AID = "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x" - SAID = "EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk" + # SAID = "EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk" # no signed headers needed for login headers = { @@ -27,36 +27,44 @@ def test_login(): "ACCEPT-ENCODING": "gzip, deflate", } - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) with pytest.raises(VerifySignedHeadersException): signed_headers_verifier.VerifySignedHeaders.handle_headers(req) - headers[ - "SIGNATURE-INPUT"] = 'signify=("@method" "@path" "signify-resource" "signify-timestamp");created=1714854033;keyid="BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE";alg="ed25519"' - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + headers["SIGNATURE-INPUT"] = ( + 'signify=("@method" "@path" "signify-resource" "signify-timestamp");created=1714854033;keyid="BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE";alg="ed25519"' + ) + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) with pytest.raises(VerifySignedHeadersException): signed_headers_verifier.VerifySignedHeaders.handle_headers(req) - headers[ - "SIGNATURE"] = 'indexed="?0";signify="0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF"' - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + headers["SIGNATURE"] = ( + 'indexed="?0";signify="0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF"' + ) + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) with pytest.raises(VerifySignedHeadersException): signed_headers_verifier.VerifySignedHeaders.handle_headers(req) headers["SIGNIFY-RESOURCE"] = "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x" - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) with pytest.raises(VerifySignedHeadersException): signed_headers_verifier.VerifySignedHeaders.handle_headers(req) headers["SIGNIFY-TIMESTAMP"] = "2024-05-04T20:20:33.730000+00:00" - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) aid, sig, ser = signed_headers_verifier.VerifySignedHeaders.handle_headers(req) assert aid == AID - assert sig == "0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF" - assert ser == '"@method": POST\n"@path": /\n"signify-resource": EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\n"signify-timestamp": 2024-05-04T20:20:33.730000+00:00\n"@signature-params: (@method @path signify-resource signify-timestamp);created=1714854033;keyid=BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE;alg=ed25519"' + assert ( + sig + == "0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF" + ) + assert ( + ser + == '"@method": POST\n"@path": /\n"signify-resource": EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\n"signify-timestamp": 2024-05-04T20:20:33.730000+00:00\n"@signature-params: (@method @path signify-resource signify-timestamp);created=1714854033;keyid=BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE;alg=ed25519"' + ) diff --git a/tests/unit/test_service.py b/tests/unit/test_service.py index 75a967d..f535adf 100644 --- a/tests/unit/test_service.py +++ b/tests/unit/test_service.py @@ -40,7 +40,7 @@ def test_ends(): app = fastapi_app.app client = TestClient(app) - result = client.get(f"/ping", headers=headers) + result = client.get("/ping", headers=headers) assert result.status_code == 200 assert result.json() == "Pong" @@ -48,7 +48,7 @@ def test_ends(): vlei_ecr = cfile.read() headers["Content-Type"] = "application/json" result = client.post( - f"/login", json={"said": SAID, "vlei": vlei_ecr}, headers=headers + "/login", json={"said": SAID, "vlei": vlei_ecr}, headers=headers ) assert result.status_code == 202 @@ -56,6 +56,4 @@ def test_ends(): assert result.status_code == 200 result = client.get(f"/upload/{AID}/{DIG}", headers=headers) - assert ( - result.status_code == 401 - ) # fail because this signature should not verify + assert result.status_code == 401 # fail because this signature should not verify diff --git a/tests/unit/test_verifying.py b/tests/unit/test_verifying.py index aff87fd..b8f31e8 100644 --- a/tests/unit/test_verifying.py +++ b/tests/unit/test_verifying.py @@ -1,14 +1,42 @@ import fastapi from starlette.datastructures import Headers -from src.regps.app.api import signed_headers_verifier +from regps.app.api.exceptions import DigestVerificationFailedException +from regps.app.api import signed_headers_verifier +import pytest +from hashlib import sha256 +from regps.app.api.digest_verifier import verify_digest + + +def test_digest_verification(): + BASE_STR = "fefUBIUhdo9032bfHf0UNONF0kubni9HnF22L0KD2".encode() + dig = sha256(BASE_STR).hexdigest() + dig = f"sha256_{dig}" + assert verify_digest(BASE_STR, dig) is True + + +def test_digest_verification_fail(): + BASE_STR = "fefUBIUhdo9032bfHf0UNONF0kubni9HnF22L0KD2".encode() + WRONG_BASE_STR = "fefUBIUhdo9032bfHf0UNONF0kubni9HnF22L0KDT".encode() + dig = sha256(BASE_STR).hexdigest() + dig = f"sha256_{dig}" + assert verify_digest(WRONG_BASE_STR, dig) is False + + +def test_digest_verification_wrong_dig(): + BASE_STR = "fefUBIUhdo9032bfHf0UNONF0kubni9HnF22L0KD2".encode() + dig = sha256(BASE_STR).hexdigest() + # Here the dig is not prefixed + with pytest.raises(DigestVerificationFailedException): + verify_digest(BASE_STR, dig) + + +def test_verify_cig(): + # AID and SAID should be the same as what is in credential.cesr for the ECR credential + # see https://trustoverip.github.io/tswg-acdc-specification/#top-level-fields to understand the fields/values + AID = "EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x" + # SAID = "EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk" -def test_verify_cig(): - #AID and SAID should be the same as what is in credential.cesr for the ECR credential - #see https://trustoverip.github.io/tswg-acdc-specification/#top-level-fields to understand the fields/values - AID="EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x" - SAID="EElnd1DKvcDzzh7u7jBjsg2X9WgdQQuhgiu80i2VR-gk" - headers = { "HOST": "localhost:7676", "CONNECTION": "keep-alive", @@ -23,9 +51,15 @@ def test_verify_cig(): "USER-AGENT": "node", "ACCEPT-ENCODING": "gzip, deflate", } - scope = dict(type='http', headers=Headers(headers).raw, method='POST', path='/') + scope = dict(type="http", headers=Headers(headers).raw, method="POST", path="/") req = fastapi.Request(scope) aid, sig, ser = signed_headers_verifier.VerifySignedHeaders.handle_headers(req) assert aid == AID - assert sig == "0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF" - assert ser == '"@method": POST\n"@path": /\n"signify-resource": EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\n"signify-timestamp": 2024-05-04T20:20:33.730000+00:00\n"@signature-params: (@method @path signify-resource signify-timestamp);created=1714854033;keyid=BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE;alg=ed25519"' \ No newline at end of file + assert ( + sig + == "0BBbeeBw3lVmQWYBpcFH9KmRXZocrqLH_LZL4aqg5W9-NMdXqIYJ-Sao7colSTJOuYllMXFfggoMhkfpTKnvPhUF" + ) + assert ( + ser + == '"@method": POST\n"@path": /\n"signify-resource": EP4kdoVrDh4Mpzh2QbocUYIv4IjLZLDU367UO0b40f6x\n"signify-timestamp": 2024-05-04T20:20:33.730000+00:00\n"@signature-params: (@method @path signify-resource signify-timestamp);created=1714854033;keyid=BPoZo2b3r--lPBpURvEDyjyDkS65xBEpmpQhHQvrwlBE;alg=ed25519"' + ) From 32d0069b9f2dcd9e40a7168076617aa71ffeaafa Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 12:55:08 -0400 Subject: [PATCH 06/11] Update publish-api.yml (#36) publish to gleif docker with user specified version --- .github/workflows/publish-api.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 3a15f96..003dc8f 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -30,7 +30,7 @@ jobs: id: meta uses: docker/metadata-action@v4 with: - images: GLEIF-it/reg-pilot-api + images: gleif/reg-pilot-api - name: Build and push Docker image uses: docker/build-push-action@v3 @@ -39,6 +39,5 @@ jobs: file: images/reg-pilot-api.dockerfile push: true tags: | - WebOfTrust/reg-poc-api:${{ github.event.inputs.version }} - WebOfTrust/reg-poc-api:latest - labels: ${{ github.event.inputs.version }} \ No newline at end of file + gleif/reg-poc-api:${{ github.event.inputs.version }} + labels: ${{ github.event.inputs.version }} From 3253937548e70a53178439c2060f90a8bfdbed62 Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 13:08:31 -0400 Subject: [PATCH 07/11] Update publish-api.yml (#37) update image name to reg-pilot-api --- .github/workflows/publish-api.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 003dc8f..b904b7e 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -39,5 +39,5 @@ jobs: file: images/reg-pilot-api.dockerfile push: true tags: | - gleif/reg-poc-api:${{ github.event.inputs.version }} + gleif/reg-pilot-api:${{ github.event.inputs.version }} labels: ${{ github.event.inputs.version }} From 749df82a98f95e077568472141cba467e4b62967 Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 13:40:00 -0400 Subject: [PATCH 08/11] Dev or specify image tag (#38) * default tag to dev for pr merge trigger, but allow user to specify tag on manual run Signed-off-by: 2byrds <2byrds@gmail.com> --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- .github/workflows/publish-api.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index b904b7e..f3ca55f 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -2,16 +2,17 @@ name: Publish Docker image on: -# push: -# branches: -# - "main" -# pull_request: -# branches: -# - "main" + push: + branches: + - "main" + pull_request: + branches: + - "main" workflow_dispatch: inputs: version: - required: true + required: false + default: 'dev' jobs: push_to_registry: name: Push Docker image to Docker Hub From b5e103609eacbfaa8ff86ed6b114b2b7c9408bb6 Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 13:50:19 -0400 Subject: [PATCH 09/11] Dev or specify image tag (#39) * default tag to dev for pr merge trigger, but allow user to specify tag on manual run --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- .github/workflows/publish-api.yml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index f3ca55f..9d2df44 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -6,8 +6,7 @@ on: branches: - "main" pull_request: - branches: - - "main" + types: [closed] workflow_dispatch: inputs: version: @@ -33,12 +32,23 @@ jobs: with: images: gleif/reg-pilot-api + - name: Determine Docker tag + id: docker_tag + run: | + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then + echo "::set-output name=tag::dev" + elif [[ -n "${{ github.event.inputs.version }}" ]]; then + echo "::set-output name=tag::${{ github.event.inputs.version }}" + else + echo "::set-output name=tag::dev" + fi + - name: Build and push Docker image uses: docker/build-push-action@v3 with: - context: . - file: images/reg-pilot-api.dockerfile - push: true - tags: | - gleif/reg-pilot-api:${{ github.event.inputs.version }} - labels: ${{ github.event.inputs.version }} + context: . + file: images/reg-pilot-api.dockerfile + push: true + tags: | + gleif/reg-pilot-api:${{ steps.docker_tag.outputs.tag }} + labels: ${{ steps.docker_tag.outputs.tag }} From daef28f51d5ac9db78394f30fb2cc4b6f924ceac Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 14:16:09 -0400 Subject: [PATCH 10/11] Dev or specify image tag (#40) * trigger action on pull request close Signed-off-by: 2byrds <2byrds@gmail.com> --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- .github/workflows/publish-api.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 9d2df44..c1c4a7e 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -6,7 +6,7 @@ on: branches: - "main" pull_request: - types: [closed] + types: [closed] workflow_dispatch: inputs: version: @@ -35,20 +35,20 @@ jobs: - name: Determine Docker tag id: docker_tag run: | - if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then - echo "::set-output name=tag::dev" - elif [[ -n "${{ github.event.inputs.version }}" ]]; then - echo "::set-output name=tag::${{ github.event.inputs.version }}" - else - echo "::set-output name=tag::dev" - fi + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then + echo "::set-output name=tag::dev" + elif [[ -n "${{ github.event.inputs.version }}" ]]; then + echo "::set-output name=tag::${{ github.event.inputs.version }}" + else + echo "::set-output name=tag::dev" + fi - name: Build and push Docker image uses: docker/build-push-action@v3 with: - context: . - file: images/reg-pilot-api.dockerfile - push: true - tags: | - gleif/reg-pilot-api:${{ steps.docker_tag.outputs.tag }} - labels: ${{ steps.docker_tag.outputs.tag }} + context: . + file: images/reg-pilot-api.dockerfile + push: true + tags: | + gleif/reg-pilot-api:${{ steps.docker_tag.outputs.tag }} + labels: ${{ steps.docker_tag.outputs.tag }} From aceccd5791f033d4213cb2795814f5a5de81fb17 Mon Sep 17 00:00:00 2001 From: Lance Date: Fri, 23 Aug 2024 15:55:06 -0400 Subject: [PATCH 11/11] point to gleif images and updated keri versions (#41) Signed-off-by: 2byrds <2byrds@gmail.com> --- docker-compose.yml | 4 ++-- images/reg-pilot-api.dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5329342..99c4888 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: build: context: . dockerfile: ./images/reg-pilot-api.dockerfile - image: 2byrds/reg-pilot-api:latest + image: gleif/reg-pilot-api:dev ports: - 8000:8000 # command: python src/regps/app/fastapi_app.py @@ -29,7 +29,7 @@ services: # - vlei-verifier vlei-verifier: - image: 2byrds/vlei-verifier:latest + image: gleif/vlei-verifier:dev container_name: vlei-verifier hostname: vlei-verifier # depends_on: diff --git a/images/reg-pilot-api.dockerfile b/images/reg-pilot-api.dockerfile index 17fa774..d9b0fdd 100644 --- a/images/reg-pilot-api.dockerfile +++ b/images/reg-pilot-api.dockerfile @@ -1,4 +1,4 @@ -FROM 2byrds/keri:1.1.7 +FROM weboftrust/keri:1.2.0-dev13 WORKDIR /usr/local/var