Skip to content

Commit

Permalink
Work /api-key to accept JWT authorization rather than email and login.
Browse files Browse the repository at this point in the history
  • Loading branch information
maxachis committed Dec 4, 2024
1 parent 614d632 commit 691f2a3
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 33 deletions.
25 changes: 13 additions & 12 deletions middleware/primary_resource_logic/api_key_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

from database_client.database_client import DatabaseClient
from database_client.helper_functions import get_db_client
from middleware.access_logic import get_token_from_request_header, AuthScheme
from middleware.access_logic import (
get_token_from_request_header,
AuthScheme,
AccessInfoPrimary,
)
from middleware.api_key import ApiKey
from middleware.exceptions import (
InvalidAPIKeyException,
Expand All @@ -25,7 +29,9 @@ def hash_api_key(api_key: str) -> str:
return hashlib.sha256(api_key.encode()).hexdigest()


def create_api_key_for_user(db_client: DatabaseClient, dto: UserRequestDTO) -> Response:
def create_api_key_for_user(
db_client: DatabaseClient, access_info: AccessInfoPrimary
) -> Response:
"""
Tries to log in a user. If successful, generates API key
Expand All @@ -34,17 +40,12 @@ def create_api_key_for_user(db_client: DatabaseClient, dto: UserRequestDTO) -> R
:param password: User's password.
:return: A response object with a message and status code.
"""
user_data = db_client.get_user_info(dto.email)

if check_password_hash(user_data.password_digest, dto.password):
api_key = ApiKey()
db_client.update_user_api_key(user_id=user_data.id, api_key=api_key.key_hash)
payload = {"api_key": api_key.raw_key}
return make_response(payload, HTTPStatus.OK)
user_id = access_info.get_user_id()

return make_response(
{"message": "Invalid email or password"}, HTTPStatus.UNAUTHORIZED
)
api_key = ApiKey()
db_client.update_user_api_key(user_id=user_id, api_key=api_key.key_hash)
payload = {"api_key": api_key.raw_key}
return make_response(payload, HTTPStatus.OK)


def api_key_is_associated_with_user(db_client: DatabaseClient, raw_key: str) -> bool:
Expand Down
16 changes: 8 additions & 8 deletions resources/ApiKeyResource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

from flask import Response

from middleware.access_logic import NO_AUTH_INFO, AccessInfoPrimary
from middleware.access_logic import (

Check warning on line 5 in resources/ApiKeyResource.py

View workflow job for this annotation

GitHub Actions / flake8

[flake8] resources/ApiKeyResource.py#L5 <401>

'middleware.access_logic.NO_AUTH_INFO' imported but unused
Raw output
./resources/ApiKeyResource.py:5:1: F401 'middleware.access_logic.NO_AUTH_INFO' imported but unused
NO_AUTH_INFO,
AccessInfoPrimary,
STANDARD_JWT_AUTH_INFO,
)
from middleware.decorators import endpoint_info
from middleware.primary_resource_logic.api_key_logic import create_api_key_for_user

Expand All @@ -22,14 +26,10 @@ class ApiKeyResource(PsycopgResource):

@endpoint_info(
namespace=namespace_api_key,
auth_info=NO_AUTH_INFO,
auth_info=STANDARD_JWT_AUTH_INFO,
description="Generates an API key for authenticated users.",
response_info=ResponseInfo(
response_dictionary={
HTTPStatus.OK.value: "OK. API key generated.",
HTTPStatus.UNAUTHORIZED.value: "Unauthorized. Forbidden or invalid authentication.",
HTTPStatus.INTERNAL_SERVER_ERROR.value: "Internal server error.",
}
success_message="OK. API key generated.",
),
schema_config=SchemaConfigs.API_KEY_POST,
)
Expand All @@ -47,5 +47,5 @@ def post(self, access_info: AccessInfoPrimary) -> Response:
"""
return self.run_endpoint(
wrapper_function=create_api_key_for_user,
schema_populate_parameters=SchemaConfigs.API_KEY_POST.value.get_schema_populate_parameters(),
access_info=access_info,
)
2 changes: 1 addition & 1 deletion resources/endpoint_schema_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ class SchemaConfigs(Enum):
input_dto_class=ResetPasswordDTO,
)
RESET_TOKEN_VALIDATION = schema_config_with_message_output()
API_KEY_POST = get_user_request_endpoint_schema_config(
API_KEY_POST = EndpointSchemaConfig(
primary_output_schema=APIKeyResponseSchema(),
)
# endregion
Expand Down
15 changes: 8 additions & 7 deletions tests/helper_scripts/helper_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ def request_reset_password_api(client_with_db, mocker, user_info):
return mock.call_args[1]["token"]


def create_api_key(client_with_db, user_info) -> str:
def create_api_key(client_with_db, jwt_authorization_header: dict) -> str:
"""
Obtain an api key for the given user, via a Flask call to the /api-key endpoint
:param client_with_db:
:param user_info:
:return: api_key
"""

response = client_with_db.post(
f"/auth{API_KEY_ROUTE}",
json={"email": user_info.email, "password": user_info.password},
f"/auth{API_KEY_ROUTE}", headers=jwt_authorization_header
)
assert (
response.status_code == HTTPStatus.OK.value
Expand Down Expand Up @@ -186,17 +186,18 @@ def create_test_user_setup(
permissions = [permissions]
for permission in permissions:
db_client.add_user_permission(user_id=user_info.user_id, permission=permission)
api_key = create_api_key(client, user_info)
jwt_tokens = login_and_return_jwt_tokens(client, user_info)
jwt_authorization_header = get_authorization_header(
scheme="Bearer", token=jwt_tokens.access_token
)
api_key = create_api_key(client, jwt_authorization_header=jwt_authorization_header)
return TestUserSetup(
user_info,
api_key,
api_authorization_header=get_authorization_header(
scheme="Basic", token=api_key
),
jwt_authorization_header=get_authorization_header(
scheme="Bearer", token=jwt_tokens.access_token
),
jwt_authorization_header=jwt_authorization_header,
)


Expand Down
15 changes: 10 additions & 5 deletions tests/integration/test_api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,34 @@
from resources.ApiKeyResource import API_KEY_ROUTE
from resources.endpoint_schema_config import SchemaConfigs
from tests.conftest import dev_db_client, flask_client_with_db
from tests.helper_scripts.helper_classes.TestDataCreatorFlask import (
TestDataCreatorFlask,
)
from tests.helper_scripts.helper_functions import (
create_test_user_db_client,
)
from tests.helper_scripts.run_and_validate_request import run_and_validate_request
from conftest import test_data_creator_flask, monkeysession

Check warning on line 14 in tests/integration/test_api_key.py

View workflow job for this annotation

GitHub Actions / flake8

[flake8] tests/integration/test_api_key.py#L14 <401>

'conftest.test_data_creator_flask' imported but unused
Raw output
./tests/integration/test_api_key.py:14:1: F401 'conftest.test_data_creator_flask' imported but unused

Check warning on line 14 in tests/integration/test_api_key.py

View workflow job for this annotation

GitHub Actions / flake8

[flake8] tests/integration/test_api_key.py#L14 <401>

'conftest.monkeysession' imported but unused
Raw output
./tests/integration/test_api_key.py:14:1: F401 'conftest.monkeysession' imported but unused


def test_api_key_post(flask_client_with_db, dev_db_client):
def test_api_key_post(test_data_creator_flask: TestDataCreatorFlask):

Check warning on line 17 in tests/integration/test_api_key.py

View workflow job for this annotation

GitHub Actions / flake8

[flake8] tests/integration/test_api_key.py#L17 <811>

redefinition of unused 'test_data_creator_flask' from line 14
Raw output
./tests/integration/test_api_key.py:17:23: F811 redefinition of unused 'test_data_creator_flask' from line 14
"""
Test that GET call to /api_key endpoint successfully creates an API key and aligns it with the user's API key in the database
"""
tdc = test_data_creator_flask

user_info = create_test_user_db_client(dev_db_client)
tus = tdc.standard_user()

response_json = run_and_validate_request(
flask_client=flask_client_with_db,
flask_client=tdc.flask_client,
http_method="post",
endpoint=f"/auth{API_KEY_ROUTE}",
json={"email": user_info.email, "password": user_info.password},
headers=tus.jwt_authorization_header,
expected_schema=SchemaConfigs.API_KEY_POST.value.primary_output_schema,
)

# Check that API key aligned with user
new_user_info = dev_db_client.get_user_info(user_info.email)
new_user_info = tdc.db_client.get_user_info(tus.user_info.email)
api_key_raw = response_json.get("api_key")
api_key = ApiKey(raw_key=api_key_raw)

Expand Down

0 comments on commit 691f2a3

Please sign in to comment.