Skip to content

Commit 22f7fd1

Browse files
authored
Merge pull request #107 from datakind/api-auth
adjusted api authentication
2 parents 9caee14 + f12709f commit 22f7fd1

2 files changed

Lines changed: 26 additions & 23 deletions

File tree

src/webapp/authn.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,24 @@
66
import jwt
77
from fastapi import Security, HTTPException, status
88
from fastapi.security import (
9-
#OAuth2PasswordBearer,
9+
# OAuth2PasswordBearer,
1010
APIKeyHeader,
11-
HTTPBearer
11+
HTTPBearer,
1212
)
1313
from passlib.context import CryptContext
1414
from pydantic import BaseModel
1515
from .config import env_vars
16-
16+
from typing import Any
1717

1818
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
1919

20-
#oauth2_apikey_scheme = OAuth2PasswordBearer(
21-
# scheme_name="api_key_scheme",
22-
#tokenUrl="token-from-api-key",
23-
#)
24-
oauth2_apikey_scheme = HTTPBearer(auto_error=True)
20+
# oauth2_apikey_scheme = OAuth2PasswordBearer(
21+
# scheme_name="api_key_scheme",
22+
# tokenUrl="token-from-api-key",
23+
# )
24+
oauth2_apikey_scheme = HTTPBearer(
25+
auto_error=True, scheme_name="Bearer token (get from /token-from-api-key)"
26+
)
2527

2628
api_key_header = APIKeyHeader(name="X-API-KEY", scheme_name="api-key", auto_error=False)
2729
# The INST value may be empty for Datakinder or cross-institution access.
@@ -59,24 +61,24 @@ def check_creds(username: str, password: str) -> bool:
5961
return username == env_vars["USERNAME"] and password == env_vars["PASSWORD"]
6062

6163

62-
def verify_password(plain_password: str, hashed_password: str) -> bool:
64+
def verify_password(plain_password: str, hashed_password: str) -> Any:
6365
"""Verify a plain password against a hash. Includes a 2y/2b replacement since Laravel
6466
Generates hashes that start with 2y. The hashing scheme recognizes both."""
6567
revert_hash = hashed_password.replace("$2y", "$2b", 1)
6668
return pwd_context.verify(plain_password, revert_hash)
6769

6870

69-
def verify_api_key(plain_api_key: str, hashed_key: str) -> bool:
71+
def verify_api_key(plain_api_key: str, hashed_key: str) -> Any:
7072
"""Verify a plain API Key against a hash."""
7173
return pwd_context.verify(plain_api_key, hashed_key)
7274

7375

74-
def get_api_key_hash(api_key: str):
76+
def get_api_key_hash(api_key: str) -> Any:
7577
"""Hash a given api key."""
7678
return pwd_context.hash(api_key)
7779

7880

79-
def get_password_hash(password: str):
81+
def get_password_hash(password: str) -> Any:
8082
"""Hash a password. To align with the password hashing used by Laravel, we have to replace the 2b
8183
generated by pwd_context with 2y and that should be the version we store.
8284
They should be functionally the same: https://stackoverflow.com/a/36225192/28478909
@@ -85,7 +87,7 @@ def get_password_hash(password: str):
8587
return initial_hash.replace("$2b", "$2y", 1)
8688

8789

88-
def create_access_token(data: dict, expires_delta: timedelta | None = None):
90+
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
8991
"""Create a JWT."""
9092
to_encode = data.copy()
9193
if expires_delta:

src/webapp/utilities.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import uuid
44
import re
5-
from typing import Annotated, Final
5+
from typing import Annotated, Final, Any
66
from urllib.parse import unquote
77
from strenum import StrEnum # needed for python pre 3.11
88
import jwt
@@ -22,7 +22,7 @@
2222
from .config import env_vars
2323

2424

25-
def decode_url_piece(src: str):
25+
def decode_url_piece(src: str) -> str:
2626
"""Decode encoded URL."""
2727
return unquote(src)
2828

@@ -165,29 +165,29 @@ class BaseUser(BaseModel):
165165
def __init__(self, usr: str | None, inst: str, access: str, email: str) -> None:
166166
super().__init__(user_id=usr, institution=inst, access_type=access, email=email)
167167

168-
def is_datakinder(self) -> bool:
168+
def is_datakinder(self) -> Any:
169169
"""Whether a given user is a Datakinder."""
170170
return self.access_type and self.access_type == AccessType.DATAKINDER
171171

172-
def is_model_owner(self) -> bool:
172+
def is_model_owner(self) -> Any:
173173
"""Whether a given user is a model owner."""
174174
return self.access_type and self.access_type == AccessType.MODEL_OWNER
175175

176-
def is_data_owner(self) -> bool:
176+
def is_data_owner(self) -> Any:
177177
"""Whether a given user is a data owner."""
178178
return self.access_type and self.access_type == AccessType.DATA_OWNER
179179

180-
def is_viewer(self) -> bool:
180+
def is_viewer(self) -> Any:
181181
"""Whether a given user is a viewer."""
182182
return self.access_type and self.access_type == AccessType.VIEWER
183183

184-
def has_access_to_inst(self, inst: str) -> bool:
184+
def has_access_to_inst(self, inst: str) -> Any:
185185
"""Whether a given user has access to a given institution."""
186186
return self.access_type and (
187187
self.access_type == AccessType.DATAKINDER or self.institution == inst
188188
)
189189

190-
def has_full_data_access(self) -> bool:
190+
def has_full_data_access(self) -> Any:
191191
"""Datakinders, model_owners, data_owners, all have full data access."""
192192
return self.access_type and self.access_type in (
193193
AccessType.DATAKINDER,
@@ -313,7 +313,9 @@ def authenticate_api_key(api_key_enduser_tuple: str, sess: Session) -> BaseUser:
313313

314314
async def get_current_user(
315315
sess: Annotated[Session, Depends(get_session)],
316-
token_from_key: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_apikey_scheme)],
316+
token_from_key: Annotated[
317+
HTTPAuthorizationCredentials, Depends(oauth2_apikey_scheme)
318+
],
317319
) -> BaseUser:
318320
"""Get the user from a given token."""
319321
credentials_exception = HTTPException(
@@ -322,7 +324,6 @@ async def get_current_user(
322324
headers={"WWW-Authenticate": "Bearer"},
323325
)
324326
usrname = None
325-
print(token_from_key)
326327
token_from_key = token_from_key.credentials
327328
try:
328329
if not token_from_key:

0 commit comments

Comments
 (0)