diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index bff9e724..ad9495f5 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -37,7 +37,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: 3.11 - name: Install Python linting dependencies run: pip install black flake8 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e8fc55c4..08ab91d0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,10 +9,10 @@ jobs: - name: Checkout git commit uses: actions/checkout@v1 - - name: Set up Python 3.7 + - name: Set up Python 3.11 uses: actions/setup-python@v1 with: - python-version: 3.7 + python-version: 3.11 - name: Install test runner run: python3 -m pip install tox diff --git a/Dockerfile b/Dockerfile index 7a55cd06..6fcf0dac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ COPY . . RUN npm run build # ----------------------------------------------------------------------------- -FROM python:3.7 as backend +FROM python:3.11 as backend RUN mkdir /opt/cosri-patientsearch WORKDIR /opt/cosri-patientsearch diff --git a/README.md b/README.md index a4800e43..c4622638 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ All views require Keycloak authentication. Keycloak roles determine authorizati 1) `git clone ` 2) `cp client_secrets.json.default client_secrets.json` # Edit to fit 3) `cp patientsearch.env.default patientsearch.env` # Edit to fit -4) `mkvirtualenv patientsearch` # Python 3.7 +4) `mkvirtualenv patientsearch` # Python 3.11 5) `pip install nodeenv` 6) `nodeenv --python-virtualenv` 7) `pip install -e .` diff --git a/patientsearch/api.py b/patientsearch/api.py index eea163fc..485d2c29 100644 --- a/patientsearch/api.py +++ b/patientsearch/api.py @@ -6,14 +6,14 @@ make_response, redirect, request, - safe_join, session, send_from_directory, ) -from flask.json import JSONEncoder +import json import jwt import requests from werkzeug.exceptions import Unauthorized, Forbidden +from werkzeug.utils import safe_join from copy import deepcopy from patientsearch.audit import audit_entry, audit_HAPI_change @@ -124,7 +124,7 @@ def home(): "templates", ), "index.html", - cache_timeout=-1, + max_age=-1, ) @@ -144,13 +144,14 @@ def user_info(): def config_settings(config_key): """Non-secret application settings""" - # workaround no JSON representation for datetime.timedelta - class CustomJSONEncoder(JSONEncoder): - def default(self, obj): + # workaround no JSON representation for datetime.timedelta and others + def serialize(obj): + try: + json.dumps(obj) + return obj + except (TypeError, OverflowError): return str(obj) - current_app.json_encoder = CustomJSONEncoder - # return selective keys - not all can be be viewed by users, e.g.secret key blacklist = ("SECRET", "KEY", "TOKEN", "CREDENTIALS") @@ -161,14 +162,14 @@ def default(self, obj): jsonify_abort( status_code=400, messag=f"Configuration key {key} not available" ) - return jsonify({key: current_app.config.get(key)}) + return jsonify({key: serialize(current_app.config.get(key))}) config_settings = {} for key in current_app.config: matches = any(pattern for pattern in blacklist if pattern in key) if matches: continue - config_settings[key] = current_app.config.get(key) + config_settings[key] = serialize(current_app.config.get(key)) return jsonify(config_settings) @@ -658,7 +659,7 @@ def logout(): "templates", ), "logout.html", - cache_timeout=-1, + max_age=-1, ) ) resp.set_cookie( @@ -690,7 +691,7 @@ def main(): "templates", ), "home.html", - cache_timeout=-1, + max_age=-1, ) @@ -703,5 +704,5 @@ def target(): "templates", ), "targetLaunch.html", - cache_timeout=-1, + max_age=-1, ) diff --git a/patientsearch/config.py b/patientsearch/config.py index 640662f0..a0b5aa1c 100644 --- a/patientsearch/config.py +++ b/patientsearch/config.py @@ -134,8 +134,7 @@ def load_json_config(potential_json_string): ) OIDC_ID_TOKEN_COOKIE_SECURE = False -OIDC_REQUIRE_VERIFIED_EMAIL = False -OIDC_SCOPES = ["email", "openid", "roles"] +OIDC_SCOPES = "email openid roles" PROJECT_NAME = os.getenv("PROJECT_NAME", "COSRI") REQUIRED_ROLES = json.loads(os.getenv("REQUIRED_ROLES", "[]")) UDS_LAB_TYPES = json.loads(os.getenv("UDS_LAB_TYPES", "[]")) diff --git a/requirements.dev.txt b/requirements.dev.txt index 86a576be..0af63f5d 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,22 +1,107 @@ # -# This file is autogenerated by pip-compile -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # -# pip-compile +# pip-compile --extra=dev --output-file=requirements.dev.txt setup.cfg # ---requirement requirements.txt - -attrs==21.2.0 # via pytest -importlib-metadata==4.8.1 # via pluggy, pytest -iniconfig==1.1.1 # via pytest -packaging==21.0 # via pytest -pluggy==1.0.0 # via pytest -py==1.10.0 # via pytest -pyparsing==2.4.7 # via packaging -pytest-datadir==1.3.1 # via patientsearch (setup.py) -pytest-flask==1.2.0 # via patientsearch (setup.py) -pytest-mock==3.6.1 # via patientsearch (setup.py) -pytest==6.2.5 # via patientsearch (setup.py), pytest-datadir, pytest-mock -toml==0.10.2 # via pytest -typing-extensions==3.10.0.2 # via importlib-metadata -zipp==3.5.0 # via importlib-metadata +async-timeout==4.0.3 + # via redis +authlib==1.3.2 + # via flask-oidc +blinker==1.8.2 + # via + # flask + # flask-oidc +cachelib==0.13.0 + # via flask-session +certifi==2024.7.4 + # via requests +cffi==1.17.1 + # via cryptography +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via flask +cryptography==44.0.0 + # via authlib +exceptiongroup==1.2.2 + # via pytest +flask==3.0.3 + # via + # flask-jwt-extended + # flask-oidc + # flask-session + # patientsearch (setup.cfg) + # pytest-flask +flask-jwt-extended==4.6.0 + # via patientsearch (setup.cfg) +git+https://github.com/uwcirg/fhir-migrations@0.1.0 +flask-oidc==2.2.2 + # via patientsearch (setup.cfg) +flask-session==0.8.0 + # via patientsearch (setup.cfg) +gunicorn==23.0.0 + # via patientsearch (setup.cfg) +idna==3.7 + # via requests +importlib-metadata==8.4.0 + # via flask +iniconfig==2.0.0 + # via pytest +itsdangerous==2.2.0 + # via flask +jinja2==3.1.4 + # via flask +jmespath==1.0.1 + # via patientsearch (setup.cfg) +markupsafe==2.1.5 + # via + # jinja2 + # werkzeug +msgspec==0.18.6 + # via flask-session +packaging==24.1 + # via + # gunicorn + # pytest +pluggy==1.5.0 + # via pytest +pycparser==2.22 + # via cffi +pyjwt==2.9.0 + # via flask-jwt-extended +pytest==8.3.2 + # via + # patientsearch (setup.cfg) + # pytest-datadir + # pytest-flask + # pytest-mock +pytest-datadir==1.5.0 + # via patientsearch (setup.cfg) +pytest-flask==1.3.0 + # via patientsearch (setup.cfg) +pytest-mock==3.14.0 + # via patientsearch (setup.cfg) +python-json-logger==2.0.7 + # via patientsearch (setup.cfg) +redis==5.0.8 + # via + # patientsearch (setup.cfg) + # redis-dict +redis-dict==2.5.0 + # via patientsearch (setup.cfg) +requests==2.32.3 + # via + # flask-oidc + # patientsearch (setup.cfg) +tomli==2.0.1 + # via pytest +urllib3==2.2.2 + # via requests +werkzeug==3.0.3 + # via + # flask + # flask-jwt-extended + # pytest-flask +zipp==3.20.0 + # via importlib-metadata diff --git a/requirements.txt b/requirements.txt index 616b9d10..83fd6b11 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,83 @@ -certifi==2021.5.30 # via requests -charset-normalizer==2.0.6 # via requests -click==8.0.1 # via flask -flask-jwt-extended==3.24.1 # via patientsearch (setup.py) -# pin to last hash of master, to remove requirement of freestanding JSON config file -git+https://github.com/uwcirg/flask-oidc.git@ed4eb20#egg=flask-oidc +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --output-file=requirements.txt setup.cfg +# +async-timeout==4.0.3 + # via redis +authlib==1.3.2 + # via flask-oidc +blinker==1.8.2 + # via + # flask + # flask-oidc +cachelib==0.13.0 + # via flask-session +certifi==2024.7.4 + # via requests +cffi==1.17.1 + # via cryptography +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via flask +cryptography==44.0.0 + # via authlib +flask==3.0.3 + # via + # flask-jwt-extended + # flask-oidc + # flask-session + # patientsearch (setup.cfg) +flask-jwt-extended==4.6.0 + # via patientsearch (setup.cfg) git+https://github.com/uwcirg/fhir-migrations@0.1.0 -flask==2.0.1 # via flask-jwt-extended, patientsearch (setup.py) -flask-session==0.3.2 # via patientsearch (setup.py) -gunicorn==20.0.4 # via patientsearch (setup.py) -idna==3.2 # via requests -importlib-metadata==4.8.1 # via click -itsdangerous==2.0.1 # via flask -jinja2==3.0.1 # via flask -jmespath==0.10.0 # via patientsearch (setup.py) -markupsafe==2.0.1 # via jinja2 -python-json-logger==0.1.11 -pyjwt==1.7.1 # via flask-jwt-extended -redis==3.5.3 -redis-dict==1.5.2 # via patientsearch (setup.py) -requests==2.26.0 # via patientsearch (setup.py) -six==1.15.0 # via flask-jwt-extended -typing-extensions==3.10.0.2 # via importlib-metadata -urllib3==1.26.7 # via requests -werkzeug==2.0.1 # via flask, flask-jwt-extended -zipp==3.5.0 # via importlib-metadata +flask-oidc==2.2.2 + # via patientsearch (setup.cfg) +flask-session==0.8.0 + # via patientsearch (setup.cfg) +gunicorn==23.0.0 + # via patientsearch (setup.cfg) +idna==3.7 + # via requests +importlib-metadata==8.4.0 + # via flask +itsdangerous==2.2.0 + # via flask +jinja2==3.1.4 + # via flask +jmespath==1.0.1 + # via patientsearch (setup.cfg) +markupsafe==2.1.5 + # via + # jinja2 + # werkzeug +msgspec==0.18.6 + # via flask-session +packaging==24.1 + # via gunicorn +pycparser==2.22 + # via cffi +pyjwt==2.9.0 + # via flask-jwt-extended +python-json-logger==2.0.7 + # via patientsearch (setup.cfg) +redis==5.0.8 + # via + # patientsearch (setup.cfg) + # redis-dict +redis-dict==2.5.0 + # via patientsearch (setup.cfg) +requests==2.32.3 + # via + # flask-oidc + # patientsearch (setup.cfg) +urllib3==2.2.2 + # via requests +werkzeug==3.0.3 + # via + # flask + # flask-jwt-extended +zipp==3.20.0 + # via importlib-metadata diff --git a/setup.cfg b/setup.cfg index 4a717be5..95719ba8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ setup_requires = install_requires = flask flask-jwt-extended + flask-oidc flask-session gunicorn requests diff --git a/tests/conftest.py b/tests/conftest.py index b62675af..b32313c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,7 +28,7 @@ def generate_claims(email, sub, roles): def generate_jwt(email="fake@testy.org", sub="fake-subject", roles=None): claims = generate_claims(email, sub, roles) encoded = jwt.encode(claims, SECRET, algorithm="HS256") - return encoded.decode("utf-8") + return encoded @pytest.fixture()