Skip to content

Commit

Permalink
Resourse DB+ Upload check validations+ (#42)
Browse files Browse the repository at this point in the history
* Resourse DB+ Upload check validations+

* Update src/regps/app/fastapi_app.py

---------

Co-authored-by: Lance <lance.byrd@gleif.org>
  • Loading branch information
aydarng and 2byrds authored Aug 30, 2024
1 parent aceccd5 commit 60626d4
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/regps/app/api/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def check_login(self, aid: str):

def login(self, said: str, vlei: str):
verifier_response = self.verifier_adapter.verify_vlei_request(said, vlei)
if verifier_response.status_code != 200:
if verifier_response.status_code != 202:
raise VerifierServiceException(
verifier_response.json(), verifier_response.status_code
)
Expand Down
33 changes: 33 additions & 0 deletions src/regps/app/api/utils/reports_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from collections import defaultdict


class ReportsDB:
def __init__(self):
self.aid_reports = defaultdict(list)
self.lei_reports = defaultdict(list)
self.aid_to_lei_mapping = dict()
self.lei_digests = defaultdict(set)

def register_aid(self, aid, lei):
self.aid_to_lei_mapping[aid] = lei

def add_report(self, aid, dig, report):
lei = self.aid_to_lei_mapping[aid] or "-"
self.aid_reports[aid].append(report)
self.lei_reports[lei].append(report)
self.lei_digests[lei].add(dig)

def drop_status(self, aid):
self.aid_reports[aid] = []
return True

def get_reports_for_aid(self, aid):
return self.aid_reports[aid]

def get_reports_for_lei(self, aid):
lei = self.aid_to_lei_mapping[aid] or "-"
return self.lei_reports[lei]

def authorized_to_check_status(self, aid, dig):
lei = self.aid_to_lei_mapping[aid] or "-"
return dig in self.lei_digests[lei]
89 changes: 80 additions & 9 deletions src/regps/app/fastapi_app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import os
from collections import defaultdict


from regps.app.api.signed_headers_verifier import logger, VerifySignedHeaders
from fastapi import (
FastAPI,
Expand All @@ -23,6 +20,7 @@
VerifierServiceException,
)
from regps.app.api.controllers import APIController
from regps.app.api.utils.reports_db import ReportsDB
from regps.app.api.utils.swagger_examples import (
check_login_examples,
upload_examples,
Expand All @@ -37,8 +35,7 @@

api_controller = APIController()
verify_signed_headers = VerifySignedHeaders(api_controller)
reports = defaultdict(list)

reports_db = ReportsDB()

@app.get("/ping")
async def ping():
Expand All @@ -56,7 +53,10 @@ async def login(response: Response, data: LoginRequest):
try:
logger.info(f"Login: sending login cred {str(data)[:50]}...")
resp = api_controller.login(data.said, data.vlei)
return JSONResponse(status_code=200, content=resp)
lei = resp.get("lei")
aid = resp.get("aid")
reports_db.register_aid(aid, lei)
return JSONResponse(status_code=202, content=resp)
except VerifierServiceException as e:
logger.error(f"Login: Exception: {e}")
response.status_code = e.status_code
Expand Down Expand Up @@ -176,7 +176,7 @@ async def upload_route(
logger.info(
f"Upload: completed upload for {aid} {dig} with code {resp.status_code}"
)
reports[aid].append(resp.json())
reports_db.add_report(aid, dig, resp.json())
return JSONResponse(status_code=200, content=resp.json())
except HTTPException as e:
logger.error(f"Upload: Exception: {e}")
Expand Down Expand Up @@ -249,12 +249,18 @@ async def check_upload_route(
"""
try:
verify_signed_headers.process_request(request, aid)
if not reports_db.authorized_to_check_status(aid, dig):
raise HTTPException(status_code=401, detail=f"AID {aid} is not authorized to check status for digest {dig}")
resp = api_controller.check_upload(aid, dig)
return JSONResponse(status_code=200, content=resp)
except VerifierServiceException as e:
logger.error(f"CheckUpload: Exception: {e}")
response.status_code = e.status_code
return JSONResponse(content=e.detail, status_code=e.status_code)
except HTTPException as e:
logger.error(f"CheckUpload: 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"CheckUpload: Exception: {e}")
raise HTTPException(status_code=500, detail=str(e))
Expand Down Expand Up @@ -312,7 +318,70 @@ async def status_route(
"""
try:
verify_signed_headers.process_request(request, aid)
resp = reports.get(aid, [])
resp = reports_db.get_reports_for_aid(aid)
return JSONResponse(status_code=202, content=resp)
except HTTPException as e:
logger.error(f"Status: 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"Status: Exception: {e}")
raise HTTPException(status_code=500, detail=str(e))


@app.get("/report/status/lei/{aid}")
async def status_for_lei_route(
request: Request,
response: Response,
aid: str = Path(
...,
description="AID",
openapi_examples={
"default": {
"summary": "Default AID. Must have logged into the verifier with a role credential specifying the LEI.",
"value": check_upload_examples["request"]["aid"],
}
},
),
signature: str = Header(
openapi_examples={
"default": {
"summary": "Default signature for signed headers.",
"value": upload_examples["request"]["headers"]["signature"],
}
}
),
signature_input: str = Header(
openapi_examples={
"default": {
"summary": "Default signature_input for signed headers.",
"value": upload_examples["request"]["headers"]["signature_input"],
}
}
),
signify_resource: str = Header(
openapi_examples={
"default": {
"summary": "Default signify_resource for signed headers.",
"value": upload_examples["request"]["headers"]["signify_resource"],
}
}
),
signify_timestamp: str = Header(
openapi_examples={
"default": {
"summary": "Default signify_timestamp for signed headers.",
"value": upload_examples["request"]["headers"]["signify_timestamp"],
}
}
),
):
"""
Check upload status by aid.
"""
try:
verify_signed_headers.process_request(request, aid)
resp = reports_db.get_reports_for_lei(aid)
return JSONResponse(status_code=202, content=resp)
except VerifierServiceException as e:
logger.error(f"Status: Exception: {e}")
Expand All @@ -326,6 +395,7 @@ async def status_route(
# TODO: Remove this endpoint when we will have DB. IT's only for tests
@app.post("/status/{aid}/drop")
def clear_status_route(
request: Request,
aid: str = Path(
...,
description="AID",
Expand All @@ -340,7 +410,8 @@ def clear_status_route(
"""
Drop upload status for specified AID. For the test purposes
"""
reports[aid] = []
verify_signed_headers.process_request(request, aid)
reports_db.drop_status(aid)
resp = {"status": "success", "aid": aid}
return JSONResponse(status_code=202, content=resp)

Expand Down
65 changes: 65 additions & 0 deletions tests/unit/test_reports_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from regps.app.api.utils.reports_db import ReportsDB


def test_check_report_status_authorization():
aid = "jnhh8f7h79nufb97hbw3fieBHJBgg7uhn"
lei = "j9h7ufehhcWBUTDVWYH98h9bfyaebgGBFfsa3wFf"
report_1 = "report 1"
dig_1 = "sha256_moiuhLFBf9afnHJDfaffg4ehgh"
report_2 = "report 2"
dig_2 = "sha256_fer4grniuojnfaNHBBcaaUh89h"
reports_db = ReportsDB()
reports_db.register_aid(aid, lei)
reports_db.add_report(aid, dig_1, report_1)
reports_db.add_report(aid, dig_2, report_2)
assert reports_db.authorized_to_check_status(aid, dig_1)
assert reports_db.authorized_to_check_status(aid, dig_2)
assert len(reports_db.get_reports_for_aid(aid)) == 2
assert len(reports_db.get_reports_for_lei(aid)) == 2


def test_check_report_status_authorization_2_aids_from_the_same_lei():
aid_1 = "jnhh8f7h79nufb97hbw3fieBHJBgg7uhn"
aid_2 = "UNBOUb8dadh98hnansudHD0jndbuh8hnd"
lei = "j9h7ufehhcWBUTDVWYH98h9bfyaebgGBFfsa3wFf"
report_1 = "report 1"
dig_1 = "sha256_moiuhLFBf9afnHJDfaffg4ehgh"
report_2 = "report 2"
dig_2 = "sha256_fer4grniuojnfaNHBBcaaUh89h"
reports_db = ReportsDB()
reports_db.register_aid(aid_1, lei)
reports_db.register_aid(aid_2, lei)
reports_db.add_report(aid_1, dig_1, report_1)
reports_db.add_report(aid_2, dig_2, report_2)
assert reports_db.authorized_to_check_status(aid_1, dig_1)
assert reports_db.authorized_to_check_status(aid_1, dig_2)
assert reports_db.authorized_to_check_status(aid_2, dig_1)
assert reports_db.authorized_to_check_status(aid_2, dig_2)
assert len(reports_db.get_reports_for_aid(aid_1)) == 1
assert len(reports_db.get_reports_for_aid(aid_2)) == 1
assert len(reports_db.get_reports_for_lei(aid_1)) == 2
assert len(reports_db.get_reports_for_lei(aid_2)) == 2


def test_check_report_status_authorization_2_aids_from_different_lei():
aid_1 = "jnhh8f7h79nufb97hbw3fieBHJBgg7uhn"
aid_2 = "UNBOUb8dadh98hnansudHD0jndbuh8hnd"
lei_1 = "j9h7ufehhcWBUTDVWYH98h9bfyaebgGBFfsa3wFf"
lei_2 = "mOI8hbsah80hihSHFIHh8h3r8hf8h08hfaiffha0"
report_1 = "report 1"
dig_1 = "sha256_moiuhLFBf9afnHJDfaffg4ehgh"
report_2 = "report 2"
dig_2 = "sha256_fer4grniuojnfaNHBBcaaUh89h"
reports_db = ReportsDB()
reports_db.register_aid(aid_1, lei_1)
reports_db.register_aid(aid_2, lei_2)
reports_db.add_report(aid_1, dig_1, report_1)
reports_db.add_report(aid_2, dig_2, report_2)
assert reports_db.authorized_to_check_status(aid_1, dig_1)
assert not reports_db.authorized_to_check_status(aid_1, dig_2)
assert not reports_db.authorized_to_check_status(aid_2, dig_1)
assert reports_db.authorized_to_check_status(aid_2, dig_2)
assert len(reports_db.get_reports_for_aid(aid_1)) == 1
assert len(reports_db.get_reports_for_aid(aid_2)) == 1
assert len(reports_db.get_reports_for_lei(aid_1)) == 1
assert len(reports_db.get_reports_for_lei(aid_2)) == 1

0 comments on commit 60626d4

Please sign in to comment.