diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 97dd28e4..e62f8301 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,7 +12,7 @@ on: # The branches below must be a subset of the branches above branches: [master] schedule: - - cron: '0 19 * * 3' + - cron: "0 19 * * 3" jobs: analyze: @@ -24,48 +24,48 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['python'] + language: ["python"] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 + - name: Checkout repository + uses: actions/checkout@v3 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 2b9c0a06..b3211b67 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -22,7 +22,7 @@ jobs: with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs - if: ${{ steps.metadata.outputs.package-ecosystem == 'pip' || steps.metadata.outputs.package-ecosystem == 'github_actions' }} + if: ${{ steps.metadata.outputs.package-ecosystem == 'pip' || steps.metadata.outputs.package-ecosystem == 'github_actions' }} run: gh pr review --approve "$PR_URL" && gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 8fd361bd..970506c6 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -4,7 +4,7 @@ # # Source repository: https://github.com/actions/dependency-review-action # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' +name: "Dependency Review" on: [pull_request] permissions: @@ -14,9 +14,9 @@ jobs: dependency-review: runs-on: ubuntu-latest steps: - - name: 'Checkout Repository' + - name: "Checkout Repository" uses: actions/checkout@v3 - - name: 'Dependency Review' + - name: "Dependency Review" uses: actions/dependency-review-action@v3 with: fail-on-severity: moderate diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a1eac033..78c41898 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,33 +8,32 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: rojopolis/spellcheck-github-actions@0.33.0 - name: Spellcheck + - uses: actions/checkout@v3 + - uses: rojopolis/spellcheck-github-actions@0.33.1 + name: Spellcheck code_docs: strategy: max-parallel: 4 matrix: os: [ubuntu-latest] - python-version: [3.8] - + python-version: ["3.10"] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Run docs tests - run: tox -e docs - \ No newline at end of file + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Run docs tests + run: tox -e docs diff --git a/.github/workflows/int.yml b/.github/workflows/int.yml index 92962db3..8f9114b7 100644 --- a/.github/workflows/int.yml +++ b/.github/workflows/int.yml @@ -9,8 +9,8 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - python-version: [3.8] - + python-version: ["3.10"] + runs-on: ${{ matrix.os }} name: Integration Tests @@ -20,54 +20,54 @@ jobs: image: registry:2 ports: - 5000:5000 - + steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install requirements - run: | - pip install aiohttp - pip install requests + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install requirements + run: | + pip install aiohttp + pip install requests + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + driver-opts: network=host + + - name: Build + uses: docker/build-push-action@v4 + with: + context: . + push: true + file: ./Dockerfile + tags: localhost:5000/beacon-python:latest + cache-from: localhost:5000/beacon-python:latest + cache-to: type=local,dest=/tmp/.buildx-cache - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - driver-opts: network=host - - - name: Build - uses: docker/build-push-action@v4 - with: - context: . - push: true - file: ./Dockerfile - tags: localhost:5000/beacon-python:latest - cache-from: localhost:5000/beacon-python:latest - cache-to: type=local,dest=/tmp/.buildx-cache - - - name: Start Services - run: | - docker-compose -f deploy/test/docker-compose.yml up -d - sleep 10 - docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata.json - docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_registered.json - docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_controlled.json - docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_controlled1.json + - name: Start Services + run: | + docker-compose -f deploy/test/docker-compose.yml up -d + sleep 10 + docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata.json + docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_registered.json + docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_controlled.json + docker exec test_beacon_1 beacon_init /exdata/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz /exdata/example_metadata_controlled1.json - - name: Run Integration test - run: | - python deploy/test/run_tests.py + - name: Run Integration test + run: | + python deploy/test/run_tests.py - - name: Collect logs from docker - if: ${{ failure() }} - run: cd deploy && docker-compose logs --no-color -t > ../tests/dockerlogs || true + - name: Collect logs from docker + if: ${{ failure() }} + run: cd deploy && docker-compose logs --no-color -t > ../tests/dockerlogs || true - - name: Persist log files - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test_debugging_help - path: tests + - name: Persist log files + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test_debugging_help + path: tests diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7dd68814..a3c442e0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,7 +6,7 @@ on: push: branches: [master] tags: - - 'v*' + - "v*" jobs: push_to_registry: @@ -18,10 +18,10 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - + - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} @@ -35,7 +35,7 @@ jobs: type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} type=raw,value=stage,enable=${{ github.ref == format('refs/heads/{0}', 'dev') }} type=ref,event=tag - + - name: Build and push uses: docker/build-push-action@v4 with: diff --git a/.github/workflows/s2i-build.yml b/.github/workflows/s2i-build.yml index eb9528fd..5389b2f7 100644 --- a/.github/workflows/s2i-build.yml +++ b/.github/workflows/s2i-build.yml @@ -8,19 +8,19 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - + runs-on: ${{ matrix.os }} name: Integration Tests - + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Install requirements - run: | - wget https://github.com/openshift/source-to-image/releases/download/v1.3.1/source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz - tar -xvf source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz - sudo cp s2i /usr/local/bin - - name: Build image - run: | - s2i build . centos/python-38-centos7 cscfi/beacon-python + - name: Install requirements + run: | + wget https://github.com/openshift/source-to-image/releases/download/v1.3.1/source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz + tar -xvf source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz + sudo cp s2i /usr/local/bin + - name: Build image + run: | + s2i build . centos/python-38-centos7 cscfi/beacon-python diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 78942b73..17004d8e 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -8,29 +8,29 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - python-version: [3.8] - + python-version: ["3.10"] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Test flake8 syntax with tox - run: tox -e flake8 - - name: Do bandit static check with tox - run: tox -e bandit - - name: Black formatting check - run: tox -e black - - name: Install libcurl-devel - run: | - sudo apt-get update - sudo apt-get install libcurl4-openssl-dev - - name: Type hints check - run: tox -e mypy + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Test flake8 syntax with tox + run: tox -e flake8 + - name: Do bandit static check with tox + run: tox -e bandit + - name: Black formatting check + run: tox -e black + - name: Install libcurl-devel + run: | + sudo apt-get update + sudo apt-get install libcurl4-openssl-dev + - name: Type hints check + run: tox -e mypy diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 5ff1cb57..e85f0033 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -3,7 +3,7 @@ name: Python Unit Tests on: push: schedule: - - cron: '0 7 * * 1' + - cron: "0 7 * * 1" jobs: unit_test: @@ -11,30 +11,30 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - python-version: [3.8] - + python-version: ["3.10"] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install libcurl-devel - run: | - sudo apt-get update - sudo apt-get install libcurl4-openssl-dev - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions coverage - - name: Run unit tests for python 3.8 - run: | - tox -e unit_tests - coverage lcov -o lcov.info - - name: Send coverage to coveralls - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.github_token }} - path-to-lcov: lcov.info + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install libcurl-devel + run: | + sudo apt-get update + sudo apt-get install libcurl4-openssl-dev + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions coverage + - name: Run unit tests for python 3.10 + run: | + tox -e unit_tests + coverage lcov -o lcov.info + - name: Send coverage to coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.github_token }} + path-to-lcov: lcov.info diff --git a/Dockerfile b/Dockerfile index 0e4f4057..c0199cd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,35 @@ -FROM python:3.8-alpine3.15 as BUILD +FROM python:3.10.12-slim as BUILD -RUN apk add --update \ - && apk add --no-cache build-base curl-dev linux-headers bash git musl-dev\ - && apk add --no-cache openssl-dev libffi-dev autoconf bzip2-dev xz-dev\ - && apk add --no-cache python3-dev rust cargo \ - && rm -rf /var/cache/apk/* +RUN apt-get update \ + && apt-get install -y build-essential bash git gcc \ + && apt-get install -y zlib1g-dev liblzma-dev libcurl4-gnutls-dev libssl-dev libffi-dev autoconf libbz2-dev xz-utils \ + && apt-get install -y python3-dev libdeflate-dev libncurses5-dev libncursesw5-dev libreadline-dev COPY requirements.txt /root/beacon/requirements.txt ENV CYTHONIZE=1 +# the following related to https://github.com/brentp/cyvcf2/issues/240#issuecomment-1534257675 +ENV LIBDEFLATE=1 -RUN pip install --upgrade pip && \ - pip install Cython==0.29.26 && \ - pip install -r /root/beacon/requirements.txt +RUN pip install --upgrade pip \ + && pip install -r /root/beacon/requirements.txt COPY setup.py /root/beacon/setup.py COPY beacon_api /root/beacon/beacon_api RUN pip install /root/beacon -FROM python:3.8-alpine3.15 +FROM python:3.10.12-slim -RUN apk add --no-cache --update bash +RUN apt-get install -y bash LABEL maintainer "CSC Developers" LABEL org.label-schema.schema-version="1.0" LABEL org.label-schema.vcs-url="https://github.com/CSCFI/beacon-python" -RUN apk add --update \ - && apk add --no-cache curl bzip2 xz +RUN apt-get update \ + && apt-get install -y curl bzip2 xz-utils -COPY --from=BUILD usr/local/lib/python3.8/ usr/local/lib/python3.8/ +COPY --from=BUILD usr/local/lib/python3.10/ usr/local/lib/python3.10/ COPY --from=BUILD /usr/local/bin/gunicorn /usr/local/bin/ diff --git a/README.md b/README.md index 2f86b872..6130dd38 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Documentation: https://beacon-python.readthedocs.io ### Quick start `beacon-python` Web Server requires: -* Python 3.8+; +* Python 3.10+; * running DB [PostgreSQL Server](https://www.postgresql.org/) 9.6+ (recommended 13). ```shell diff --git a/beacon_api/app.py b/beacon_api/app.py index 357e5179..977448b1 100644 --- a/beacon_api/app.py +++ b/beacon_api/app.py @@ -135,7 +135,7 @@ def main(): if __name__ == "__main__": - if sys.version_info < (3, 8): - LOG.error("beacon-python requires python 3.8") + if sys.version_info < (3, 10): + LOG.error("beacon-python requires python 3.10") sys.exit(1) main() diff --git a/beacon_api/conf/__init__.py b/beacon_api/conf/__init__.py index 6b02a8cd..fba5bbd7 100644 --- a/beacon_api/conf/__init__.py +++ b/beacon_api/conf/__init__.py @@ -63,13 +63,14 @@ def parse_oauth2_config_file(path: str) -> Any: """Parse configuration file.""" config = ConfigParser() config.read(path) - config_vars: Dict[str, Union[str, bool, None]] = { + config_vars: Dict[str, Union[str, bool, None, List[str]]] = { "server": config.get("oauth2", "server"), "issuers": config.get("oauth2", "issuers"), "userinfo": config.get("oauth2", "userinfo"), "audience": config.get("oauth2", "audience") or None, "verify_aud": bool(strtobool(config.get("oauth2", "verify_aud"))), "bona_fide_value": config.get("oauth2", "bona_fide_value"), + "trusted_jkus": config.get("oauth2", "trusted_jkus", fallback="").split(","), } return convert(config_vars) diff --git a/beacon_api/conf/config.ini b/beacon_api/conf/config.ini index c4a715f2..277db7fe 100644 --- a/beacon_api/conf/config.ini +++ b/beacon_api/conf/config.ini @@ -7,7 +7,7 @@ title=GA4GHBeacon at CSC # Version of the Beacon implementation -version=1.8.0 +version=1.10.0 # Author of this software author=CSC developers @@ -122,3 +122,8 @@ audience= # If your service is not part of any network or AAI, but you still want to use tokens # produced by other AAI parties, set this value to False to skip the audience validation step verify_aud=False + +# Comma separated list of trusted JKUs for checking passports +# Passport with an untrusted JKU will be denied access +# Leave empty to disable JKU checking +trusted_jkus= diff --git a/beacon_api/permissions/ga4gh.py b/beacon_api/permissions/ga4gh.py index 4ec6def6..07917f9f 100644 --- a/beacon_api/permissions/ga4gh.py +++ b/beacon_api/permissions/ga4gh.py @@ -163,6 +163,13 @@ async def get_ga4gh_permissions(token: str) -> Tuple[set, bool]: for encoded_passport in encoded_passports: # Decode passport header, payload = await decode_passport(encoded_passport) + # If trusted_jkus variable is set, only allow passports with a trusted JKU + if not OAUTH2_CONFIG.trusted_jkus == [""]: + # Skip passports with untrusted JKUs + passport_jku = header.get("jku") + if passport_jku not in OAUTH2_CONFIG.trusted_jkus: + LOG.debug("Untrusted JKU.") + continue # Sort passports that carry dataset permissions pass_type = payload.get("ga4gh_visa_v1", {}).get("type") if pass_type == "ControlledAccessGrants": # nosec diff --git a/deploy/dataloader/Dockerfile b/deploy/dataloader/Dockerfile index cb87e44d..a2238c84 100644 --- a/deploy/dataloader/Dockerfile +++ b/deploy/dataloader/Dockerfile @@ -1,6 +1,6 @@ FROM cscfi/beacon-python -RUN apk add --no-cache --update curl-dev lftp +RUN apt-get install update libcurl-dev lftp RUN wget https://raw.githubusercontent.com/CSCfi/beacon-python/master/data/example_metadata.json RUN wget https://raw.githubusercontent.com/CSCfi/beacon-python/master/data/init.sql diff --git a/docs/example.rst b/docs/example.rst index 330b6dfa..b788321b 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -126,7 +126,7 @@ Example Response: "createdAt": "2019-09-04T12:00:00Z", "updatedAt": "2019-09-05T05:55:18Z", "environment": "prod", - "version": "1.7.2" + "version": "1.10.0" } Query Endpoint diff --git a/docs/instructions.rst b/docs/instructions.rst index 54e5f8fa..7e829c65 100644 --- a/docs/instructions.rst +++ b/docs/instructions.rst @@ -3,7 +3,7 @@ Instructions .. note:: In order to run ``beacon-python`` Web Server requirements are as specified below: - * Python 3.8+; + * Python 3.10+; * running DB `PostgreSQL Server `_ 9.6+ (recommended 13). .. _env-setup: diff --git a/readthedocs.yml b/readthedocs.yml index 1773a400..0413e75c 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -4,7 +4,7 @@ build: image: latest requirements_file: docs/docs.txt python: - version: 3.8 + version: 3.10 use_system_site_packages: true setup_py_install: true extra_requirements: diff --git a/requirements.txt b/requirements.txt index 59f80252..8feca5fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ aiohttp==3.8.4 aiohttp-cors==0.7.0 asyncpg==0.28.0 -jsonschema==4.18.0 -Cython==0.29.36 -cyvcf2==0.30.18 +jsonschema==4.18.3 +Cython==3.0.0 +cyvcf2==0.30.22 uvloop==0.17.0 aiocache==0.11.1 ujson==5.8.0 Authlib==1.2.1 -gunicorn==20.1.0 +gunicorn==21.0.1 diff --git a/setup.py b/setup.py index adcbfedf..5f0afd91 100644 --- a/setup.py +++ b/setup.py @@ -33,26 +33,25 @@ "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Scientific/Engineering :: Bio-Informatics", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.10", ], install_requires=[ "asyncpg==0.28.0", "aiohttp==3.8.4", "Authlib==1.2.1", "aiohttp-cors==0.7.0", - "jsonschema==4.18.0", - "gunicorn==20.1.0", + "jsonschema==4.18.3", + "gunicorn==21.0.1", "uvloop==0.17.0", - "cyvcf2==0.30.18", + "cyvcf2==0.30.22", "aiocache==0.11.1", "ujson==5.8.0", ], extras_require={ "vcf": [ "numpy==1.25.1", - "cyvcf2==0.30.18", - "Cython==0.29.36", + "cyvcf2==0.30.22", + "Cython==3.0.0", ], "test": [ "coverage==7.2.7", @@ -63,7 +62,7 @@ "flake8==6.0.0", "flake8-docstrings==1.7.0", "aioresponses==0.7.4", - "black==23.3.0", + "black==23.7.0", ], "docs": ["sphinx >= 1.4", "sphinx_rtd_theme==1.2.2"], }, diff --git a/tests/test.ini b/tests/test.ini index 8aa4ad7a..278cedb3 100644 --- a/tests/test.ini +++ b/tests/test.ini @@ -7,7 +7,7 @@ title=GA4GHBeacon at CSC # Version of the Beacon implementation -version=1.7.2 +version=1.10.0 # Author of this software author=CSC developers @@ -116,3 +116,8 @@ audience= # If your service is not part of any network or AAI, but you still want to use tokens # produced by other AAI parties, set this value to False to skip the audience validation step verify_aud=False + +# Comma separated list of trusted JKUs for checking passports +# Passport with an untrusted JKU will be denied access +# Leave empty to disable JKU checking +trusted_jkus=http://test.csc.fi/jwk diff --git a/tests/test_app.py b/tests/test_app.py index 8a1f0d7d..575c51bc 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -5,7 +5,7 @@ import json from authlib.jose import jwt import os -from test.support import EnvironmentVarGuard +from test.support.os_helper import EnvironmentVarGuard from aiocache import caches diff --git a/tests/test_basic.py b/tests/test_basic.py index 575e9e9f..1f1d95e1 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -8,7 +8,8 @@ from beacon_api.permissions.ga4gh import check_ga4gh_token, decode_passport, get_ga4gh_permissions from .test_app import PARAMS, generate_token from testfixtures import TempDirectory -from test.support import EnvironmentVarGuard +from test.support.os_helper import EnvironmentVarGuard +from beacon_api.conf import OAUTH2_CONFIG def mock_token(bona_fide, permissions, auth): @@ -70,6 +71,11 @@ async def load_datafile(self, vcf, datafile, datasetId, n=1000, min_ac=1): return ["datasetId", "variants"] +async def mock_get_ga4gh_controlled(input): + """Mock retrieve dataset permissions.""" + return input + + class TestBasicFunctions(unittest.IsolatedAsyncioTestCase): """Test supporting functions.""" @@ -482,5 +488,50 @@ async def test_get_ga4gh_permissions(self, m_userinfo, m_decode, m_controlled, m self.assertEqual(bona_fide_status, True) +class TestCaseCheckJku(unittest.IsolatedAsyncioTestCase): + """Test case.""" + + @unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_bona_fide") + @unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_controlled", side_effect=mock_get_ga4gh_controlled) + @unittest.mock.patch("beacon_api.permissions.ga4gh.decode_passport") + @unittest.mock.patch("beacon_api.permissions.ga4gh.retrieve_user_data") + async def test_jku_check(self, m_userinfo, m_decode, m_controller, m_bonafide): + """Test trusted and untrusted jku.""" + # Test: trusted jku + m_userinfo.return_value = [""] + header = {"jku": "http://test.csc.fi/jwk"} + payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}} + m_decode.return_value = header, payload + m_bonafide.return_value = False + dataset_permissions, bona_fide_status = await get_ga4gh_permissions({}) + self.assertEqual(dataset_permissions, [("", header)]) + self.assertEqual(bona_fide_status, False) + # Test: untrusted jku + m_userinfo.return_value = [""] + header = {"jku": "untrusted_jku"} + payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}} + m_decode.return_value = header, payload + m_bonafide.return_value = False + dataset_permissions, bona_fide_status = await get_ga4gh_permissions({}) + self.assertEqual(dataset_permissions, []) + self.assertEqual(bona_fide_status, False) + + @unittest.mock.patch("beacon_api.permissions.ga4gh.OAUTH2_CONFIG", new=OAUTH2_CONFIG._replace(trusted_jkus=[""])) + @unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_bona_fide") + @unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_controlled", side_effect=mock_get_ga4gh_controlled) + @unittest.mock.patch("beacon_api.permissions.ga4gh.decode_passport") + @unittest.mock.patch("beacon_api.permissions.ga4gh.retrieve_user_data") + async def test_jku_check_not_active(self, m_userinfo, m_decode, m_controller, m_bonafide): + """Test if jku check is skipped when trusted_jkus config var is not set.""" + m_userinfo.return_value = [""] + header = {"jku": "untrusted_jku"} + payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}} + m_decode.return_value = header, payload + m_bonafide.return_value = False + dataset_permissions, bona_fide_status = await get_ga4gh_permissions({}) + self.assertEqual(dataset_permissions, [("", header)]) + self.assertEqual(bona_fide_status, False) + + if __name__ == "__main__": unittest.main() diff --git a/tox.ini b/tox.ini index 2abdedb1..e3f0069d 100644 --- a/tox.ini +++ b/tox.ini @@ -56,4 +56,4 @@ commands = py.test -x --cov=beacon_api tests/ --cov-fail-under=80 [gh-actions] python = - 3.8: flake8, unit_tests, docs, bandit, mypy + 3.10: flake8, unit_tests, docs, bandit, mypy