Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Work /api-key to accept JWT authorization rather than email and login. #211

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@

@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 @@
"""
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
Loading