Loading .coveragerc +3 −1 Original line number Diff line number Diff line [report] omit = tests/* app/brokers/local/* No newline at end of file app/brokers/filesys/* app/auth/keycloak.py app/main.py No newline at end of file app/auth/__init__.py 0 → 100644 +3 −0 Original line number Diff line number Diff line from app.auth.abstract import AuthBroker __all__ = ["AuthBroker"] app/auth/abstract.py 0 → 100644 +11 −0 Original line number Diff line number Diff line from abc import ABC, abstractmethod from app.common import UserInfo class AuthBroker(ABC): """an abstract class for auth broker""" @abstractmethod def process_user_token(self, token: str) -> UserInfo: pass app/auth/keycloak.py 0 → 100644 +39 −0 Original line number Diff line number Diff line import jwt import requests from app.auth import AuthBroker from app.common import AuthError, ServiceError, UserInfo from app.settings import settings class KeycloakBroker(AuthBroker): """Keycloak broker""" def _refresh_token(self, token: str) -> UserInfo: data = { "grant_type": "refresh_token", "client_id": settings.keycloak_client, "client_secret": settings.keycloak_secret, "refresh_token": token, } response = requests.post(settings.keycloak_url, data=data, timeout=3) token = response.json()["access_token"] username = jwt.decode(token, options={"verify_signature": False})[ "preferred_username" ] return UserInfo(user_name=username) def process_user_token(self, token: str) -> UserInfo: if not settings.rdb_authorize: return UserInfo(user_name=token) try: user_info = self._refresh_token(token) except requests.HTTPError as e: print(e.response.status_code) if e.response.status_code == 401: raise AuthError from e raise ServiceError from e except Exception as e: raise ServiceError from e return user_info app/brokers/filesys/broker.py +19 −7 Original line number Diff line number Diff line Loading @@ -2,8 +2,9 @@ import base64 import os import shutil from app.auth import AuthBroker from app.brokers import RemoteDataBroker from app.common import DownloadData, UploadData from app.common import DownloadData, UploadData, UserInfo from app.settings import settings Loading @@ -15,9 +16,9 @@ def b64d(value: str) -> str: return base64.b64decode(value).decode() def encode_filename(data: UploadData) -> str: def encode_filename(user_info: UserInfo, data: UploadData) -> str: encoded = ( b64e(data.user) b64e(user_info.user_name) + "%" + b64e(data.dataset) + "%" Loading @@ -31,7 +32,8 @@ def decode_filename(encoded: str) -> str: return decoded def copy_complete(source: str, target: str) -> None: def copy_complete(_user_info: UserInfo, source: str, target: str) -> None: # TODO : check user from _user_info allowed to read/write a file # copy content, stat-info (mode too), timestamps... shutil.copy2(source, target) # copy owner and group Loading @@ -42,13 +44,23 @@ def copy_complete(source: str, target: str) -> None: class FilesysBroker(RemoteDataBroker): """a broker that uses a (local) filesystem to store remote data""" def __init__(self, broker: AuthBroker): self._auth_broker = broker def _authorize(self, token: str) -> UserInfo: return self._auth_broker.process_user_token(token) def upload(self, data: UploadData) -> str: uid = encode_filename(data) copy_complete(data.input_path, settings.rdb_storage_path + "/" + uid) user_info = self._authorize(data.token) uid = encode_filename(user_info, data) copy_complete(user_info, data.input_path, settings.rdb_storage_path + "/" + uid) return uid def download(self, data: DownloadData) -> None: user_info = self._authorize(data.token) fname = decode_filename(data.uid) copy_complete( settings.rdb_storage_path + "/" + data.uid, data.output_path + "/" + fname user_info, settings.rdb_storage_path + "/" + data.uid, data.output_path + "/" + fname, ) Loading
.coveragerc +3 −1 Original line number Diff line number Diff line [report] omit = tests/* app/brokers/local/* No newline at end of file app/brokers/filesys/* app/auth/keycloak.py app/main.py No newline at end of file
app/auth/__init__.py 0 → 100644 +3 −0 Original line number Diff line number Diff line from app.auth.abstract import AuthBroker __all__ = ["AuthBroker"]
app/auth/abstract.py 0 → 100644 +11 −0 Original line number Diff line number Diff line from abc import ABC, abstractmethod from app.common import UserInfo class AuthBroker(ABC): """an abstract class for auth broker""" @abstractmethod def process_user_token(self, token: str) -> UserInfo: pass
app/auth/keycloak.py 0 → 100644 +39 −0 Original line number Diff line number Diff line import jwt import requests from app.auth import AuthBroker from app.common import AuthError, ServiceError, UserInfo from app.settings import settings class KeycloakBroker(AuthBroker): """Keycloak broker""" def _refresh_token(self, token: str) -> UserInfo: data = { "grant_type": "refresh_token", "client_id": settings.keycloak_client, "client_secret": settings.keycloak_secret, "refresh_token": token, } response = requests.post(settings.keycloak_url, data=data, timeout=3) token = response.json()["access_token"] username = jwt.decode(token, options={"verify_signature": False})[ "preferred_username" ] return UserInfo(user_name=username) def process_user_token(self, token: str) -> UserInfo: if not settings.rdb_authorize: return UserInfo(user_name=token) try: user_info = self._refresh_token(token) except requests.HTTPError as e: print(e.response.status_code) if e.response.status_code == 401: raise AuthError from e raise ServiceError from e except Exception as e: raise ServiceError from e return user_info
app/brokers/filesys/broker.py +19 −7 Original line number Diff line number Diff line Loading @@ -2,8 +2,9 @@ import base64 import os import shutil from app.auth import AuthBroker from app.brokers import RemoteDataBroker from app.common import DownloadData, UploadData from app.common import DownloadData, UploadData, UserInfo from app.settings import settings Loading @@ -15,9 +16,9 @@ def b64d(value: str) -> str: return base64.b64decode(value).decode() def encode_filename(data: UploadData) -> str: def encode_filename(user_info: UserInfo, data: UploadData) -> str: encoded = ( b64e(data.user) b64e(user_info.user_name) + "%" + b64e(data.dataset) + "%" Loading @@ -31,7 +32,8 @@ def decode_filename(encoded: str) -> str: return decoded def copy_complete(source: str, target: str) -> None: def copy_complete(_user_info: UserInfo, source: str, target: str) -> None: # TODO : check user from _user_info allowed to read/write a file # copy content, stat-info (mode too), timestamps... shutil.copy2(source, target) # copy owner and group Loading @@ -42,13 +44,23 @@ def copy_complete(source: str, target: str) -> None: class FilesysBroker(RemoteDataBroker): """a broker that uses a (local) filesystem to store remote data""" def __init__(self, broker: AuthBroker): self._auth_broker = broker def _authorize(self, token: str) -> UserInfo: return self._auth_broker.process_user_token(token) def upload(self, data: UploadData) -> str: uid = encode_filename(data) copy_complete(data.input_path, settings.rdb_storage_path + "/" + uid) user_info = self._authorize(data.token) uid = encode_filename(user_info, data) copy_complete(user_info, data.input_path, settings.rdb_storage_path + "/" + uid) return uid def download(self, data: DownloadData) -> None: user_info = self._authorize(data.token) fname = decode_filename(data.uid) copy_complete( settings.rdb_storage_path + "/" + data.uid, data.output_path + "/" + fname user_info, settings.rdb_storage_path + "/" + data.uid, data.output_path + "/" + fname, )