diff --git a/Dockerfile.mac_chip b/Dockerfile.mac_chip new file mode 100644 index 0000000..0b13f51 --- /dev/null +++ b/Dockerfile.mac_chip @@ -0,0 +1,22 @@ +# Use this Dockerfile if you are using a Mac with an Apple chip + +FROM python:3.11-alpine + +WORKDIR /usr/src/app + +RUN apk add --update --no-cache --virtual .tmp-build-deps \ + gcc libc-dev linux-headers postgresql-dev \ + && apk add libffi-dev + +RUN pip install poetry + +COPY poetry.lock pyproject.toml ./ + +RUN poetry config virtualenvs.create false +RUN poetry install --no-root + +COPY ./src ./ + +EXPOSE 8888 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8888"] \ No newline at end of file diff --git a/README.md b/README.md index 7f9c77a..2420ffd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ poetry run uvicorn main:app --reload --port 8888 ``` ### Local development notes - [.env.template](.env.template) is added to allow VS Code to search the correct path for imports when writing tests, just copy the [.env.template](.env.template) file into `.env` file locally -- [src/.env.tempalte](./src/.env.template) is added as the template for the app's environment variables, appropriate values are already provided in [.env.local](./src/.env.local) for local development. If `ENV` variable with default of `LOCAL` is changed, copy this template into `src/.env`, and provide appropriate values, and set all listed empty variables in the environment. +- [src/.env.template](./src/.env.template) is added as the template for the app's environment variables, appropriate values are already provided in [.env.local](./src/.env.local) for local development. If `ENV` variable with default of `LOCAL` is changed, copy this template into `src/.env`, and provide appropriate values, and set all listed empty variables in the environment. --- ## Retrieve credentials diff --git a/src/dependencies.py b/src/dependencies.py index 9739f4b..384c7c9 100644 --- a/src/dependencies.py +++ b/src/dependencies.py @@ -1,4 +1,3 @@ -import os from http import HTTPStatus from typing import Annotated from fastapi import Depends, HTTPException, Request @@ -7,6 +6,12 @@ from entities.engine import get_session from entities.repos import institutions_repo as repo +OPEN_DOMAIN_REQUESTS = { + "/v1/admin/me": {"GET"}, + "/v1/institutions": {"GET"}, + "/v1/institutions/domains/allowed": {"GET"}, +} + async def check_domain( request: Request, session: Annotated[AsyncSession, Depends(get_session)] @@ -21,16 +26,10 @@ async def check_domain( def request_needs_domain_check(request: Request) -> bool: - open_endpoints = os.getenv( - "OPEN_ENDPOINT_PATHS", - "/v1/admin/me,/v1/institutions,/v1/institutions/domains/allowed", - ) - open_methods = os.getenv("OPEN_ENDPOINT_METHODS", "GET") - endpoints = open_endpoints.split(",") - methods = open_methods.split(",") + path = request.scope["path"].rstrip("/") return not ( - request.scope["path"].rstrip("/") in endpoints - and request.scope["method"] in methods + path in OPEN_DOMAIN_REQUESTS + and request.scope["method"] in OPEN_DOMAIN_REQUESTS[path] ) diff --git a/src/entities/repos/institutions_repo.py b/src/entities/repos/institutions_repo.py index bad3c6f..887809b 100644 --- a/src/entities/repos/institutions_repo.py +++ b/src/entities/repos/institutions_repo.py @@ -78,7 +78,7 @@ async def add_domains( async def is_email_domain_allowed(session: AsyncSession, email: str) -> bool: domain = get_email_domain(email) - if domain is not None: + if domain: async with session: stmt = select(func.count()).filter(DeniedDomainDao.domain == domain) res = await session.scalar(stmt) @@ -87,6 +87,6 @@ async def is_email_domain_allowed(session: AsyncSession, email: str) -> bool: def get_email_domain(email: str) -> str: - if email is not None: + if email: return email.split("@")[-1] return None diff --git a/src/oauth2/oauth2_admin.py b/src/oauth2/oauth2_admin.py index 80c765b..f2cbe3c 100644 --- a/src/oauth2/oauth2_admin.py +++ b/src/oauth2/oauth2_admin.py @@ -30,7 +30,7 @@ def get_claims(self, token: str) -> Dict[str, str] | None: key=self._get_keys(), issuer=os.getenv("KC_REALM_URL"), audience=os.getenv("AUTH_CLIENT"), - options={"verify_at_hash": False, "verify_aud": False}, + options={"verify_at_hash": False, "verify_aud": False, "verify_iss": False}, ) except jose.ExpiredSignatureError: pass diff --git a/tests/api/routers/test_institutions_api.py b/tests/api/routers/test_institutions_api.py index 02ae664..a666e44 100644 --- a/tests/api/routers/test_institutions_api.py +++ b/tests/api/routers/test_institutions_api.py @@ -90,8 +90,8 @@ def test_get_institution_unauthed( self, app_fixture: FastAPI, unauthed_user_mock: Mock ): client = TestClient(app_fixture) - leiPath = "testLeiPath" - res = client.get(f"/v1/institutions/{leiPath}") + lei_path = "testLeiPath" + res = client.get(f"/v1/institutions/{lei_path}") assert res.status_code == 403 def test_get_institution_authed( @@ -108,16 +108,17 @@ def test_get_institution_authed( ], ) client = TestClient(app_fixture) - leiPath = "testLeiPath" - res = client.get(f"/v1/institutions/{leiPath}") + lei_path = "testLeiPath" + res = client.get(f"/v1/institutions/{lei_path}") assert res.status_code == 200 assert res.json().get("name") == "Test Bank 123" def test_add_domains_unauthed(self, app_fixture: FastAPI, unauthed_user_mock: Mock): client = TestClient(app_fixture) - leiPath = "testLeiPath" + + lei_path = "testLeiPath" res = client.post( - f"/v1/institutions/{leiPath}/domains/", json=[{"domain": "testDomain"}] + f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] ) assert res.status_code == 403 @@ -129,9 +130,10 @@ def test_add_domains_authed( FinancialInstitutionDomainDao(domain="test.bank", lei="TESTBANK123") ] client = TestClient(app_fixture) - leiPath = "testLeiPath" + + lei_path = "testLeiPath" res = client.post( - f"/v1/institutions/{leiPath}/domains/", json=[{"domain": "testDomain"}] + f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] ) assert res.status_code == 200 assert res.json()[0].get("domain") == "test.bank" @@ -150,9 +152,9 @@ def test_add_domains_authed_no_permission( AuthenticatedUser.from_claim(claims), ) client = TestClient(app_fixture) - leiPath = "testLeiPath" + lei_path = "testLeiPath" res = client.post( - f"/v1/institutions/{leiPath}/domains/", json=[{"domain": "testDomain"}] + f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] ) assert res.status_code == 403 @@ -162,9 +164,9 @@ def test_add_domains_authed_with_denied_email_domain( domain_denied_mock = mocker.patch("dependencies.email_domain_denied") domain_denied_mock.return_value = True client = TestClient(app_fixture) - leiPath = "testLeiPath" + lei_path = "testLeiPath" res = client.post( - f"/v1/institutions/{leiPath}/domains/", json=[{"domain": "testDomain"}] + f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] ) assert res.status_code == 403 assert "domain denied" in res.json()["detail"]