From 4fa6699cb5a43416080e01d7f10ab37598cd310b Mon Sep 17 00:00:00 2001 From: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:52:30 +0000 Subject: [PATCH] ci: enhance the security of gh workflows (#10564) * ci: enhance security of workflows * ci: fix docs git add of search folders * chore: remove jans-tent * docs: update docs with the removal of jans tent * ci: fix pip upgrade Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix ignore previously installed packages Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up dep in build of assets Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up dep in build of assets Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix clean up Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix permission level for clean up Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: skip deleting if the service doesn't have any packages Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: load all pages Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: load all pages Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: load all pages Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> --------- Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> --- .github/dependabot.yml | 4 - .github/workflows/build-docker-image.yml | 18 +- .github/workflows/build-docs.yml | 5 +- .github/workflows/build-packages.yml | 8 +- .github/workflows/build-test.yml | 67 +++-- .github/workflows/lint-flak8.yml | 17 +- .github/workflows/ops-docs.yml | 4 +- .github/workflows/ops-label-pr-issues.yml | 10 +- .github/workflows/release.yaml | 7 +- .github/workflows/sanitary-github-cache.yml | 3 +- .github/workflows/sanitary-workflow-runs.yml | 2 + .github/workflows/scan-sonar.yml | 7 +- .github/workflows/test-cedarling.yml | 4 +- .github/workflows/test-jans-pycloudlib.yml | 2 +- README.md | 1 - demos/README.md | 8 +- demos/jans-tent/.flaskenv | 2 - demos/jans-tent/.gitignore | 146 --------- demos/jans-tent/LICENSE | 201 ------------- demos/jans-tent/README.md | 144 --------- demos/jans-tent/behave.ini | 3 - demos/jans-tent/clientapp/__init__.py | 251 ---------------- demos/jans-tent/clientapp/config.py | 36 --- demos/jans-tent/clientapp/helpers/__init__.py | 0 .../clientapp/helpers/cgf_checker.py | 17 -- .../clientapp/helpers/client_handler.py | 117 -------- .../clientapp/helpers/custom_msg_factory.py | 59 ---- demos/jans-tent/clientapp/templates/home.html | 21 -- demos/jans-tent/clientapp/utils/__init__.py | 0 .../clientapp/utils/dcr_from_config.py | 41 --- demos/jans-tent/clientapp/utils/logger.py | 16 - .../docs/images/authorize_code_flow.png | Bin 53305 -> 0 bytes demos/jans-tent/main.py | 6 - demos/jans-tent/register_new_client.py | 12 - demos/jans-tent/requirements.txt | 119 -------- .../tests/behaver/features/environment.py | 31 -- .../tests/behaver/features/oidc_auth.feature | 40 --- .../features/passport_social_auth.feature | 26 -- .../tests/behaver/features/steps/allow.py | 116 -------- .../tests/unit_integration/helper.py | 189 ------------ .../test_callback_endpoint.py | 62 ---- .../unit_integration/test_cfg_checker.py | 0 .../test_client_register_endpoint.py | 145 --------- .../tests/unit_integration/test_config.py | 19 -- .../test_configuration_endpoint.py | 107 ------- .../unit_integration/test_dcr_from_config.py | 76 ----- .../test_dynamic_client_registration.py | 277 ------------------ .../unit_integration/test_flask_factory.py | 93 ------ .../test_gluu_preselected_provider.py | 46 --- .../unit_integration/test_logout_endpoint.py | 65 ---- .../test_protected_content_endpoint.py | 68 ----- .../agama/quick-start-using-agama-lab.md | 2 +- 52 files changed, 103 insertions(+), 2617 deletions(-) delete mode 100644 demos/jans-tent/.flaskenv delete mode 100644 demos/jans-tent/.gitignore delete mode 100644 demos/jans-tent/LICENSE delete mode 100644 demos/jans-tent/README.md delete mode 100644 demos/jans-tent/behave.ini delete mode 100644 demos/jans-tent/clientapp/__init__.py delete mode 100644 demos/jans-tent/clientapp/config.py delete mode 100644 demos/jans-tent/clientapp/helpers/__init__.py delete mode 100644 demos/jans-tent/clientapp/helpers/cgf_checker.py delete mode 100644 demos/jans-tent/clientapp/helpers/client_handler.py delete mode 100644 demos/jans-tent/clientapp/helpers/custom_msg_factory.py delete mode 100644 demos/jans-tent/clientapp/templates/home.html delete mode 100644 demos/jans-tent/clientapp/utils/__init__.py delete mode 100644 demos/jans-tent/clientapp/utils/dcr_from_config.py delete mode 100644 demos/jans-tent/clientapp/utils/logger.py delete mode 100644 demos/jans-tent/docs/images/authorize_code_flow.png delete mode 100644 demos/jans-tent/main.py delete mode 100644 demos/jans-tent/register_new_client.py delete mode 100644 demos/jans-tent/requirements.txt delete mode 100644 demos/jans-tent/tests/behaver/features/environment.py delete mode 100644 demos/jans-tent/tests/behaver/features/oidc_auth.feature delete mode 100644 demos/jans-tent/tests/behaver/features/passport_social_auth.feature delete mode 100644 demos/jans-tent/tests/behaver/features/steps/allow.py delete mode 100644 demos/jans-tent/tests/unit_integration/helper.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_callback_endpoint.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_cfg_checker.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_config.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_dcr_from_config.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_flask_factory.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_logout_endpoint.py delete mode 100644 demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f33fe6fac22..97049410915 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,10 +26,6 @@ updates: schedule: interval: daily - - package-ecosystem: pip - directory: /demos/jans-tent - schedule: - interval: daily - package-ecosystem: docker directory: /docker-jans-all-in-one diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index d398da52eca..6a0dfef7cb1 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -57,7 +57,7 @@ jobs: egress-policy: audit - name: Install Cosign - uses: sigstore/cosign-installer@v3.5.0 + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -91,9 +91,9 @@ jobs: if: steps.build_docker_image.outputs.build || github.event_name == 'tags' run: | sudo apt-get update - sudo python3 -m pip install --upgrade pip - sudo pip3 install setuptools --upgrade - sudo pip3 install -r ./automation/requirements.txt + sudo python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" + sudo pip3 install --ignore-installed setuptools --upgrade + sudo pip3 install --ignore-installed -r ./automation/requirements.txt sudo apt-get update #- uses: actions/delete-package-versions@v5 @@ -165,19 +165,19 @@ jobs: fi # UPDATE BUILD DATES INSIDE THE DOCKERFILE BEFORE BUILDING THE DEV IMAGES TRIGGERED BY JENKINS - - name: Setup Python 3.7 + - name: Setup Python 3.10 if: github.event_name == 'workflow_dispatch' && ${{ matrix.docker-images }} != 'loadtesting-jmeter' uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: - python-version: 3.7 + python-version: "3.10" - name: Install Python dependencies if: github.event_name == 'workflow_dispatch' && ${{ matrix.docker-images }} != 'loadtesting-jmeter' run: | sudo apt-get update - sudo python3 -m pip install --upgrade pip - sudo pip3 install setuptools --upgrade - sudo pip3 install -r ./automation/requirements.txt + sudo python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" + sudo pip3 install --ignore-installed setuptools --upgrade + sudo pip3 install --ignore-installed -r ./automation/requirements.txt sudo apt-get update sudo apt-get install jq diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index b1b771aa930..28e9ce016b9 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -209,7 +209,10 @@ jobs: # END move generated chart from a previous step # copy search from nightly to all other versions. This is to ensure that the search index is available for all versions - for folder in v*/; do cp -r nightly/search "$folder"; done + for folder in v*/; do + cp -r nightly/search "$folder" + git add $folder/search && git update-index --refresh + done # END copy search from nightly to all other versions echo "Replacing release number markers with actual release number" diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 6d5687ec882..258caa392d2 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -5,6 +5,8 @@ on: tags: - 'v**' - 'nightly' +permissions: + contents: read jobs: publish_binary_packages: if: github.repository == 'JanssenProject/jans' @@ -196,7 +198,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y python3 build-essential ca-certificates dbus systemd iproute2 gpg python3-pip python3-dev libpq-dev gcc - python3 -m pip install --upgrade pip + python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" pip3 install shiv wheel setuptools echo "Building jans-linux-setup package" sudo chown -R runner:docker /home/runner/work/jans/jans @@ -356,8 +358,8 @@ jobs: git_user_signingkey: true git_commit_gpgsign: true - - uses: actions/setup-python@v5 - - uses: PyO3/maturin-action@v1 + - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + - uses: PyO3/maturin-action@ea5bac0f1ccd0ab11c805e2b804bfcb65dac2eab # v1.45.0 with: working-directory: ${{ github.workspace }}/jans-cedarling/bindings/cedarling_python command: build diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 0142d4bc5b4..845e8702a16 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -40,35 +40,50 @@ on: concurrency: group: run-once cancel-in-progress: false +permissions: + contents: read jobs: cleanup: - if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' runs-on: ubuntu-20.04 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: read + packages: write steps: - name: Harden Runner uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 with: egress-policy: audit - name: Get version ID for 0.0.0-nightly - if: github.event_name == 'push' && github.ref == 'refs/heads/main' id: get_version_id run: | - services=$(gh api -H "Accept: application/vnd.github+json" \ - /orgs/JanssenProject/packages?package_type=maven \ - | jq -r '.[].name') - for service in "${services}"; do - version_id=$(gh api -H "Accept: application/vnd.github+json" \ - /orgs/JanssenProject/packages/maven/io.jans.${service}/versions \ - | jq -r '.[] | select(.name == "0.0.0-nightly") | .id') - echo "version_id=$version_id" >> $GITHUB_ENV - gh api --method DELETE \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - /orgs/JanssenProject/packages/maven/io.jans."${service}"/versions/"${version_id}" + page=1 + services="" + while true; do + response=$(gh api -H "Accept: application/vnd.github+json" \ + /orgs/JanssenProject/packages?package_type=maven\&per_page=100\&page=$page) + names=$(echo "$response" | jq -r '.[].name') + if [ -z "$names" ]; then + break + fi + services="$services $names" + page=$((page + 1)) done - + + services=$(echo "$services" | tr '\n' ' ' | sed 's/ *$//') + echo "Services: $services" + for service in $services; do + echo "Checking $service" + version_id=$(gh api -H "Accept: application/vnd.github+json" \ + /orgs/JanssenProject/packages/maven/"${service}"/versions \ + | jq -r '.[] | select(.name == "0.0.0-nightly") | .id') + echo "version_id=$version_id" >> $GITHUB_ENV + gh api --method DELETE \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /orgs/JanssenProject/packages/maven/"${service}"/versions/"${version_id}" || echo "Failed to delete $service" + done prep-matrix: needs: cleanup @@ -126,18 +141,18 @@ jobs: with: egress-policy: audit - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ github.event.inputs.branch }} - name: Set up Java 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: java-version: '17' distribution: 'adopt' - name: Set up Maven - uses: actions/setup-java@v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: java-version: '17' distribution: 'adopt' @@ -159,7 +174,7 @@ jobs: - name: Archive results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: build-results path: ${{ matrix.service }}/target @@ -170,7 +185,9 @@ jobs: run-tests: if: github.event_name == 'push' || github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && github.event.inputs.project == 'jans-bom, jans-orm, jans-core, jans-lock/lock-server, agama, jans-auth-server, jans-link, jans-fido2, jans-scim, jans-keycloak-link, jans-config-api, jans-keycloak-integration, jans-casa') - permissions: write-all + permissions: + contents: read + packages: write needs: cleanup runs-on: ubuntu-20.04 env: @@ -198,18 +215,18 @@ jobs: with: egress-policy: audit - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ github.event.inputs.branch }} - name: Set up Java 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: java-version: '17' distribution: 'adopt' - name: Set up Maven - uses: actions/setup-java@v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: java-version: '17' distribution: 'adopt' @@ -276,13 +293,13 @@ jobs: ls /tmp/reports/ - name: Upload Test Results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ matrix.persistence }}-test-results path: /tmp/reports - name: Publish Test Report ${{ matrix.persistence }} - uses: starburstdata/action-testng-report@v1 + uses: starburstdata/action-testng-report@f245422953fb97ec5075d07782a1b596124b7cc4 # v1.0.5 with: report_paths: /tmp/reports/${{ matrix.persistence }}*.xml github_token: ${{ github.token }} diff --git a/.github/workflows/lint-flak8.yml b/.github/workflows/lint-flak8.yml index 63dadc76b64..e8eff713621 100644 --- a/.github/workflows/lint-flak8.yml +++ b/.github/workflows/lint-flak8.yml @@ -4,14 +4,16 @@ on: branches: - main paths: - #TODO: add all python projects paths below "jans-pycloudlib", "jans-cli-tui", "jans-linux-setup" - - 'demos/jans-tent/**' + - 'jans-pycloudlib/**' + - 'jans-cli-tui/**' + - 'jans-linux-setup/**' pull_request: branches: - main paths: - #TODO: add all python projects paths below "jans-pycloudlib", "jans-cli-tui", "jans-linux-setup" - - 'demos/jans-tent/**' + - 'jans-pycloudlib/**' + - 'jans-cli-tui/**' + - 'jans-linux-setup/**' permissions: contents: read @@ -23,8 +25,11 @@ jobs: #max-parallel: 1 fail-fast: false matrix: - #TODO: add all python projects paths below "jans-pycloudlib", "jans-cli-tui", "jans-linux-setup" - python-projects: ["demos/jans-tent"] + python-projects: [ + "jans-pycloudlib", + "jans-cli-tui", + "jans-linux-setup" + ] steps: - name: Harden Runner uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 diff --git a/.github/workflows/ops-docs.yml b/.github/workflows/ops-docs.yml index 07c61013b3f..34311ad3e04 100644 --- a/.github/workflows/ops-docs.yml +++ b/.github/workflows/ops-docs.yml @@ -71,10 +71,10 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.7 + - name: Set up Python 3.10 uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: - python-version: 3.7 + python-version: "3.10" - name: Auto-merge inhouse doc prs run: | diff --git a/.github/workflows/ops-label-pr-issues.yml b/.github/workflows/ops-label-pr-issues.yml index 73528021229..bf0b1cd5f83 100644 --- a/.github/workflows/ops-label-pr-issues.yml +++ b/.github/workflows/ops-label-pr-issues.yml @@ -31,17 +31,17 @@ jobs: - name: check out code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Setup Python 3.7 + - name: Set up Python 3.10 uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: - python-version: 3.7 + python-version: "3.10" - name: Install dependencies run: | sudo apt-get update - sudo python3 -m pip install --upgrade pip - sudo pip3 install setuptools --upgrade - sudo pip3 install -r ./automation/requirements.txt + sudo python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" + sudo pip3 install --ignore-installed setuptools --upgrade + sudo pip3 install --ignore-installed -r ./automation/requirements.txt sudo apt-get update sudo apt-get install jq curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ca0b9fcd917..d7df2a159cb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,12 +10,17 @@ jobs: strategy: fail-fast: false steps: + - name: Harden Runner + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - - uses: googleapis/release-please-action@v4 + - uses: googleapis/release-please-action@7987652d64b4581673a76e33ad5e98e3dd56832f # v4.1.3 id: release-please with: release-type: simple diff --git a/.github/workflows/sanitary-github-cache.yml b/.github/workflows/sanitary-github-cache.yml index b2bfb70f57d..e1dd3fa9676 100644 --- a/.github/workflows/sanitary-github-cache.yml +++ b/.github/workflows/sanitary-github-cache.yml @@ -4,7 +4,8 @@ on: types: - closed workflow_dispatch: - +permissions: + contents: read jobs: cleanup: runs-on: ubuntu-latest diff --git a/.github/workflows/sanitary-workflow-runs.yml b/.github/workflows/sanitary-workflow-runs.yml index fd3137becc7..c8115cc62a8 100644 --- a/.github/workflows/sanitary-workflow-runs.yml +++ b/.github/workflows/sanitary-workflow-runs.yml @@ -3,6 +3,8 @@ on: schedule: - cron: '0 0 */2 * *' workflow_dispatch: +permissions: + contents: read jobs: del_runs: runs-on: ubuntu-latest diff --git a/.github/workflows/scan-sonar.yml b/.github/workflows/scan-sonar.yml index 66284080304..69cac4cfc36 100644 --- a/.github/workflows/scan-sonar.yml +++ b/.github/workflows/scan-sonar.yml @@ -55,7 +55,8 @@ on: - '!**.txt' workflow_dispatch: - +permissions: + contents: read jobs: sonar-scan: name: sonar scan @@ -82,7 +83,9 @@ jobs: jans-linux-setup jans-cli-tui jans-pycloudlib - + permissions: + contents: read + pull-requests: read steps: - name: Harden Runner uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 diff --git a/.github/workflows/test-cedarling.yml b/.github/workflows/test-cedarling.yml index 6647eba00c0..896caa6c449 100644 --- a/.github/workflows/test-cedarling.yml +++ b/.github/workflows/test-cedarling.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable - name: Run Tests run: | cd ./jans-cedarling @@ -45,7 +45,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python3 -m pip install --upgrade pip + python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" python3 -m pip install tox - name: Test with pytest run: | diff --git a/.github/workflows/test-jans-pycloudlib.yml b/.github/workflows/test-jans-pycloudlib.yml index 3603b64f320..b673adb249f 100644 --- a/.github/workflows/test-jans-pycloudlib.yml +++ b/.github/workflows/test-jans-pycloudlib.yml @@ -41,7 +41,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python3 -m pip install --upgrade pip + python3 -m pip install --upgrade pip || echo "Failed to upgrade pip" python3 -m pip install tox - name: Test with pytest run: | diff --git a/README.md b/README.md index 44b01972fad..5aca229dbed 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ commercial distribution of Janssen Project Components called | **[Jans Lock](jans-lock)** | An enterprise authorization solution featuring the Cedarling, a stateless PDP and the Lock Server which centralizes audit logs and configuration. | ![Incubating](https://img.shields.io/badge/Incubating-%23f79307) | | **[Jans Tarp](demos/jans-tarp)** | An OpenID Connect RP test website that runs as a browser plugin in Chrome or Firefox. | ![Incubating](https://img.shields.io/badge/Incubating-%23f79307) | | **[Jans Chip](demos/jans-chip)** | Sample iOS and Android mobile applications that implement the full OAuth and FIDO security stack for app integrity, client constrained access tokens, and user presence. | ![Demo](https://img.shields.io/badge/Demo-%23368af7) | -| **[Jans Tent](demos/jans-tent)** | A test Relying Party ("RP") built using Python and Flask. Enables you to send different requests by quickly modifying just one configuration file. | ![Demo](https://img.shields.io/badge/Demo-%23368af7) | ## Installation diff --git a/demos/README.md b/demos/README.md index d16501ccf25..66b93674826 100644 --- a/demos/README.md +++ b/demos/README.md @@ -4,6 +4,10 @@ This folder holds different demos for different applications with janssen author ## [Benchmarking](benchmarking) Holds a docker load test image packaging for Janssen. This image can load test users to a janssen environment and can execute jmeter tests. -## [Jans-tent](jans-tent) -Reliable OpenID client to be used in auth testing. +## [Janssen Chip](jans-chip) +- A first party android mobile application that leverages dynamic client registration (DCR), DPoP access tokens. +- Passkey authentication + +## [Janssen Tarp](jans-tarp) +A Relying Party tool in form of a Browser Extension for convenient testing of authentication flows on a browser. diff --git a/demos/jans-tent/.flaskenv b/demos/jans-tent/.flaskenv deleted file mode 100644 index bc1b2cf6e71..00000000000 --- a/demos/jans-tent/.flaskenv +++ /dev/null @@ -1,2 +0,0 @@ -#.flaskenv -FLASK_APP=clientapp diff --git a/demos/jans-tent/.gitignore b/demos/jans-tent/.gitignore deleted file mode 100644 index 6b3dc1fcd19..00000000000 --- a/demos/jans-tent/.gitignore +++ /dev/null @@ -1,146 +0,0 @@ -#jans-tent-specific -client_info.json -*.log.* - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -.vscode/ -.scannerwork - diff --git a/demos/jans-tent/LICENSE b/demos/jans-tent/LICENSE deleted file mode 100644 index 6912a5f93c9..00000000000 --- a/demos/jans-tent/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2023 Christian Eland - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/demos/jans-tent/README.md b/demos/jans-tent/README.md deleted file mode 100644 index 3f6c6c1ff87..00000000000 --- a/demos/jans-tent/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# Jans Tent - -To test an OpenID Provider ("OP"), you need a test Relying Party ("RP"). Jans -Tent is easy to configure RP which enables you to send different requests by -quickly modifying one file (`config.py`). It's a Python Flask application, -so it's easy to hack for other testing requirements. - -By default, it uses `localhost` as the `redirect_uri`, so if you run it on your -laptop, all you need to do is specify the OP hostname to run it. Tent uses -dynamic client registration to obtain client credentials. But you can also use -an existing client_id if you like. - -## Installation - -**Important**: Ensure you have `Python >= 3.11` - -**Mac Users**: We recommend using [pyenv - simple python version management](https://github.com/pyenv/pyenv) instead of Os x native python. - -1. Navigate to the project root folder `jans/demos/jans-tent` -2. Create virtual environment -```bash -python3 -m venv venv -```` -3. Activate the virtual virtual environment -```bash -source venv/bin/activate -``` -4. Install dependencies -```bash -pip install -r requirements.txt -``` - -## Setup - -### 1. Edit configuration file `clientapp/config.py` according to your needs: - * Set `ISSUER`, replace `op_hostname` (required) - * Set any other desired configuration - -### 2. Generate test RP server self signed certs - -Generate `key.pem` and `cert.pem` at `jans-tent` project root folder (`jans/demos/jans-tent`). i.e: -```bash -openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -``` - -### 3. Import your OP TLS certificate - -(remember to be inside your virtual environment) - -Supply the hostname of the ISSUER after the `=` - -```bash -export OP_HOSTNAME= -``` - -```bash -echo | openssl s_client -servername $OP_HOSTNAME -connect $OP_HOSTNAME:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > op_web_cert.cer -``` - -```bash -export CERT_PATH=$(python3 -m certifi) -``` - -```bash -export SSL_CERT_FILE=${CERT_PATH} -``` - -```bash -export REQUESTS_CA_BUNDLE=${CERT_PATH} && mv op_web_cert.cer $CERT_PATH -``` - -## Using the server - -### Start the server - -Please notice that your client will be automatically registered once the server -starts. If your client was already registered, when you start the server again, -it won't register. Remember to be inside your virtual environment! - -```bash -python main.py -``` - -### Login! - -Navigate your browser to `https://localhost:9090` and click the link to start. - -## Manual client configuration - -In case your OP doesn't support dynamic registration, manually configure your -client by creating a file caled `client_info.json` in the `jans-tent` folder -with the following claims: - -```json -{ - "op_metadata_url": "https://op_hostname/.well-known/openid-configuration", - "client_id": "e4f2c3a9-0797-4c6c-9268-35c5546fb3e9", - "client_secret": "a3e71cf1-b9b4-44c5-a9e6-4c7b5c660a5d" -} -``` - -## Updating Tent to use a different OP - -If you want to test a different OP, do the following: - -1. Remove `op_web_cert` from the tent folder, and follow the procedure above -to download and install a new OP TLS certificate -2. Remove `client_info.json` from the tent folder -3. Update the value of `ISSUER` in `./clientapp/config.py` -4. Run `./register_new_client.py` - -## Other Tent endpoints - -### Auto-register endpoint - -Sending a `POST` request to Jans Tent `/register` endpoint containing a `JSON` -with the OP/AS url and client url, like this: - -```json -{ - "op_url": "https://OP_HOSTNAME", - "client_url": "https://localhost:9090", - "additional_params": { - "scope": "openid mail profile" - } -} -``` -Please notice that `additional_params` is not required by endpoint. - -The response will return the registered client id and client secret - -### Auto-config endpoint - -Sending a `POST` request to the Tent `/configuration` endpoint, containing the -client id, client secret, and metadata endpoint will fetch data from OP metadata -url and override the `config.py` settings during runtime. - -```json -{ - "client_id": "e4f2c3a9-0797-4c6c-9268-35c5546fb3e9", - "client_secret": "5c9e4775-0f1d-4a56-87c9-a629e1f88b9b", - "op_metadata_url": "https://OP_HOSTNAME/.well-known/openid-configuration" -} -``` diff --git a/demos/jans-tent/behave.ini b/demos/jans-tent/behave.ini deleted file mode 100644 index cbb1bc67a71..00000000000 --- a/demos/jans-tent/behave.ini +++ /dev/null @@ -1,3 +0,0 @@ -[behave] -stderr_capture=False -stdout_capture=False diff --git a/demos/jans-tent/clientapp/__init__.py b/demos/jans-tent/clientapp/__init__.py deleted file mode 100644 index a7429e815a5..00000000000 --- a/demos/jans-tent/clientapp/__init__.py +++ /dev/null @@ -1,251 +0,0 @@ -''' -Project: Test Auth Client -Author: Christian Hawk - - -Licensed under the Apache License, Version 2.0 (the 'License'); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an 'AS IS' BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -''' -import base64 -import urllib -import json -import os -from urllib.parse import urlparse -from authlib.integrations.flask_client import OAuth -from flask import (Flask, jsonify, redirect, render_template, request, session, - url_for) -from . import config as cfg -from .helpers.client_handler import ClientHandler -from .helpers.cgf_checker import register_client_if_no_client_info -from .utils.logger import setup_logger - -setup_logger() - -oauth = OAuth() - - -def add_config_from_json(): - with open('client_info.json', 'r') as openfile: - client_info = json.load(openfile) - cfg.SERVER_META_URL = client_info['op_metadata_url'] - cfg.CLIENT_ID = client_info['client_id'] - cfg.CLIENT_SECRET = client_info['client_secret'] - cfg.END_SESSION_ENDPOINT = client_info['end_session_endpoint'] # separate later - - -def get_preselected_provider(): - provider_id_string = cfg.PRE_SELECTED_PROVIDER_ID - provider_object = '{ "provider" : "%s" }' % provider_id_string - provider_object_bytes = provider_object.encode() - base64url_bytes = base64.urlsafe_b64encode(provider_object_bytes) - base64url_value = base64url_bytes.decode() - # if base64url_value.endswith('='): - # base64url_value_unpad = base64url_value.replace('=', '') - # return base64url_value_unpad - return base64url_value - - -def get_provider_host(): - provider_host_string = cfg.PROVIDER_HOST_STRING - provider_object = '{ "providerHost" : "%s" }' % provider_host_string - provider_object_bytes = provider_object.encode() - base64url_bytes = base64.urlsafe_b64encode(provider_object_bytes) - base64url_value = base64url_bytes.decode() - # if base64url_value.endswith('='): - # base64url_value_unpad = base64url_value.replace('=', '') - # return base64url_value_unpad - return base64url_value - - -def ssl_verify(ssl_verify=cfg.SSL_VERIFY): - if ssl_verify is False: - os.environ['CURL_CA_BUNDLE'] = "" - - -class BaseClientErrors(Exception): - status_code = 500 - - -def create_app(): - register_client_if_no_client_info() - add_config_from_json() - ssl_verify() - - app = Flask(__name__) - - app.secret_key = b'fasfafpj3rasdaasfglaksdgags331s' - app.config['OP_CLIENT_ID'] = cfg.CLIENT_ID - app.config['OP_CLIENT_SECRET'] = cfg.CLIENT_SECRET - oauth.init_app(app) - oauth.register( - 'op', - server_metadata_url=cfg.SERVER_META_URL, - client_kwargs={ - 'scope': cfg.SCOPE - }, - token_endpoint_auth_method=cfg.SERVER_TOKEN_AUTH_METHOD - ) - - @app.route('/') - def index(): - user = session.get('user') - id_token = session.get('id_token') - return render_template("home.html", user=user, id_token=id_token) - - @app.route('/logout') - def logout(): - app.logger.info('Called /logout') - if 'id_token' in session.keys(): - app.logger.info('Cleaning session credentials') - token_hint = session.get('id_token') - session.pop('id_token') - session.pop('user') - parsed_redirect_uri = urllib.parse.urlparse(cfg.REDIRECT_URIS[0]) - post_logout_redirect_uri = '%s://%s' % (parsed_redirect_uri.scheme, parsed_redirect_uri.netloc) - return redirect( - '%s?post_logout_redirect_uri=%s&token_hint=%s' % ( - cfg.END_SESSION_ENDPOINT, post_logout_redirect_uri, token_hint - ) - ) - - app.logger.info('Not authorized to logout, redirecting to index') - return redirect(url_for('index')) - - @app.route('/register', methods=['POST']) - def register(): - app.logger.info('/register called') - content = request.json - app.logger.debug('data = %s' % content) - status = 0 - data = '' - if content is None: - status = 400 - # message = 'No json data posted' - elif 'op_url' and 'redirect_uris' not in content: - status = 400 - # message = 'Not needed keys found in json' - else: - app.logger.info('Trying to register client %s on %s' % - (content['redirect_uris'], content['op_url'])) - op_url = content['op_url'] - redirect_uris = content['redirect_uris'] - - op_parsed_url = urlparse(op_url) - client_parsed_redirect_uri = urlparse(redirect_uris[0]) - - if op_parsed_url.scheme != 'https' or client_parsed_redirect_uri.scheme != 'https': - status = 400 - - elif ((( - op_parsed_url.path != '' or op_parsed_url.query != '') or client_parsed_redirect_uri.path == '') or client_parsed_redirect_uri.query != ''): - status = 400 - - else: - additional_metadata = {} - if 'additional_params' in content.keys(): - additional_metadata = content['additional_params'] - client_handler = ClientHandler( - content['op_url'], content['redirect_uris'], additional_metadata - ) - data = client_handler.get_client_dict() - status = 200 - return jsonify(data), status - - @app.route('/protected-content', methods=['GET']) - def protected_content(): - app.logger.debug('/protected-content - cookies = %s' % request.cookies) - app.logger.debug('/protected-content - session = %s' % session) - if 'user' in session: - return session['user'] - - return redirect(url_for('login')) - - @app.route('/login') - def login(): - app.logger.info('/login requested') - redirect_uri = cfg.REDIRECT_URIS[0] - app.logger.debug('/login redirect_uri = %s' % redirect_uri) - # response = oauth.op.authorize_redirect() - query_args = { - 'redirect_uri': redirect_uri, - } - - if cfg.ACR_VALUES is not None: - query_args['acr_values'] = cfg.ACR_VALUES - - # used for inbound-saml, uncomment and set config.py to use it - # if cfg.PRE_SELECTED_PROVIDER is True: - # query_args[ - # 'preselectedExternalProvider'] = get_preselected_provider() - - # used for gluu-passport, , uncomment and set config.py to use it - # if cfg.PROVIDER_HOST_STRING is not None: - # query_args["providerHost"] = get_provider_host() - - if cfg.ADDITIONAL_PARAMS is not None: - query_args |= cfg.ADDITIONAL_PARAMS - - response = oauth.op.authorize_redirect(**query_args) - - app.logger.debug('/login authorize_redirect(redirect_uri) url = %s' % - (response.location)) - - return response - - @app.route('/oidc_callback') - @app.route('/callback') - def callback(): - try: - if not request.args['code']: - return {}, 400 - - app.logger.info('/callback - received %s - %s' % - (request.method, request.query_string)) - token = oauth.op.authorize_access_token() - app.logger.debug('/callback - token = %s' % token) - user = oauth.op.userinfo() - app.logger.debug('/callback - user = %s' % user) - session['user'] = user - session['id_token'] = token['userinfo'] - app.logger.debug('/callback - cookies = %s' % request.cookies) - app.logger.debug('/callback - session = %s' % session) - - return redirect('/') - - except Exception as error: - app.logger.error(str(error)) - return {'error': str(error)}, 400 - - @app.route("/configuration", methods=["POST"]) - def configuration(): - # Receives client configuration via API - app.logger.info('/configuration called') - content = request.json - app.logger.debug("content = %s" % content) - if content is not None: - if 'provider_id' in content: - cfg.PRE_SELECTED_PROVIDER_ID = content['provider_id'] - cfg.PRE_SELECTED_PROVIDER = True - app.logger.debug('/configuration: provider_id = %s' % - content['provider_id']) - - return jsonify({"provider_id": content['provider_id']}), 200 - - if "client_id" in content and "client_secret" in content: - # Setup client_id and client_secret - oauth.op.client_id = content['client_id'] - oauth.op.client_secret = content['client_secret'] - return {}, 200 - else: - return {}, 400 - - return app diff --git a/demos/jans-tent/clientapp/config.py b/demos/jans-tent/clientapp/config.py deleted file mode 100644 index 04bcb8df3b9..00000000000 --- a/demos/jans-tent/clientapp/config.py +++ /dev/null @@ -1,36 +0,0 @@ -# REQUIRED -# Replace op_hostname -ISSUER = 'https://op_hostname' - -# Tent redirect uri -REDIRECT_URIS = [ - 'https://localhost:9090/oidc_callback' -] - -# OPTIONAL: Use at your own risk - -# Token authentication method can be -# client_secret_basic -# client_secret_post -# none -SERVER_TOKEN_AUTH_METHOD = 'client_secret_post' - -# ACR VALUES -# Examples: -# ACR_VALUES = "agama" -# ACR_VALUES = 'simple_password_auth' -ACR_VALUES = None - -# ADDITIONAL PARAMS TO CALL AUTHORIZE ENDPOINT, WITHOUT BASE64 ENCODING. USE DICT {'param': 'value'} -# ADDITIONAL_PARAMS = {'paramOne': 'valueOne', 'paramTwo': 'valueTwo'} -ADDITIONAL_PARAMS = None - -# SYSTEM SETTINGS -# use with caution, unsecure requests, for development environments -SSL_VERIFY = False - -# SCOPES -# Only scope "openid" is required for a pairwise identifier from the OP. -# OP can provision additional optional scopes as needed. -# SCOPE = 'openid email profile' -SCOPE = 'openid' diff --git a/demos/jans-tent/clientapp/helpers/__init__.py b/demos/jans-tent/clientapp/helpers/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/demos/jans-tent/clientapp/helpers/cgf_checker.py b/demos/jans-tent/clientapp/helpers/cgf_checker.py deleted file mode 100644 index e5ade597adf..00000000000 --- a/demos/jans-tent/clientapp/helpers/cgf_checker.py +++ /dev/null @@ -1,17 +0,0 @@ -from os.path import exists -import logging -from clientapp.utils.dcr_from_config import register - -logger = logging.getLogger(__name__) - - -def configuration_exists() -> bool: - return exists('client_info.json') - - -def register_client_if_no_client_info() -> None: - if configuration_exists() : - logger.info('Found configuration file client_info.json, skipping auto-register') - else: - logger.info('Client configuration not found, trying to auto-register through DCR') - register() diff --git a/demos/jans-tent/clientapp/helpers/client_handler.py b/demos/jans-tent/clientapp/helpers/client_handler.py deleted file mode 100644 index 7e5f8f12e2a..00000000000 --- a/demos/jans-tent/clientapp/helpers/client_handler.py +++ /dev/null @@ -1,117 +0,0 @@ -''' -Project: Test Auth Client -Author: Christian Hawk - - -Licensed under the Apache License, Version 2.0 (the 'License'); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an 'AS IS' BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -''' -import logging -import json -from httplib2 import RelativeURIError -from typing import Optional, Dict, Any - -from oic.oauth2 import ASConfigurationResponse -from oic.oic import Client -from oic.utils.authn.client import CLIENT_AUTHN_METHOD -from .custom_msg_factory import CustomMessageFactory - - -logger = logging.getLogger(__name__) - - -class ClientHandler: - __redirect_uris = None - __client_id = None - __client_secret = None - __metadata_url = None - __op_url = None - __additional_metadata = None - __end_session_endpoint = None - op_data = None - - def __init__(self, op_url: str, redirect_uris: list[str], additional_metadata: dict): - """[initializes] - - :param op_url: [url from oidc provider starting with https] - :type op_url: str - :param redirect_uris: [url from client starting with https] - :type redirect_uris: list - :param additional_metadata: additional client metadata - :type additional_metadata: dict - """ - self.__additional_metadata = additional_metadata - self.clientAdapter = Client(client_authn_method=CLIENT_AUTHN_METHOD, message_factory=CustomMessageFactory) - self.__op_url = op_url - self.__redirect_uris = redirect_uris - self.__metadata_url = '%s/.well-known/openid-configuration' % op_url - self.op_data = self.discover(op_url) - self.reg_info = self.register_client(op_data=self.op_data, redirect_uris=redirect_uris) - self.__end_session_endpoint = self.op_data['end_session_endpoint'] - self.__client_id = self.reg_info['client_id'] - self.__client_secret = self.reg_info['client_secret'] - - def get_client_dict(self) -> dict: - r = { - 'op_metadata_url': self.__metadata_url, - 'client_id': self.__client_id, - 'client_secret': self.__client_secret, - 'end_session_endpoint': self.__end_session_endpoint - } - - return r - - def register_client(self, op_data: ASConfigurationResponse = op_data, redirect_uris: Optional[list[str]] = __redirect_uris) -> dict: - """[register client and returns client information] - - :param op_data: [description] - :type op_data: dict - :param redirect_uris: [description] - :type redirect_uris: list[str] - :return: [client information including client-id and secret] - :rtype: dict - """ - logger.debug('called ClientHandler´s register_client method') - registration_args = {'redirect_uris': redirect_uris, - 'response_types': ['code'], - 'grant_types': ['authorization_code'], - 'application_type': 'web', - 'client_name': 'Jans Tent', - 'token_endpoint_auth_method': 'client_secret_post', - **self.__additional_metadata - } - logger.info('calling register with registration_args: %s', json.dumps(registration_args, indent=2)) - reg_info = self.clientAdapter.register(op_data['registration_endpoint'], **registration_args) - logger.info('register_client - reg_info = %s', json.dumps(reg_info.to_dict(), indent=2)) - return reg_info - - def discover(self, op_url: Optional[str] = __op_url) -> ASConfigurationResponse: - """Discover op information on .well-known/open-id-configuration - :param op_url: [description], defaults to __op_url - :type op_url: str, optional - :return: [data retrieved from OP url] - :rtype: ASConfigurationResponse - """ - logger.debug('called discover') - try: - op_data = self.clientAdapter.provider_config(op_url) - return op_data - - except json.JSONDecodeError as err: - logger.error('Error trying to decode JSON: %s' % err) - - except RelativeURIError as err: - logger.error(err) - - except Exception as e: - logging.error('An unexpected ocurred: %s' % e) - diff --git a/demos/jans-tent/clientapp/helpers/custom_msg_factory.py b/demos/jans-tent/clientapp/helpers/custom_msg_factory.py deleted file mode 100644 index 11f0ae09a60..00000000000 --- a/demos/jans-tent/clientapp/helpers/custom_msg_factory.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Custom message factory required by pyoic to add scope param -Overrides RegistrationRequest, RegistrationResponse -and use them to create CustomMessageFactory -""" - -from oic.oic.message import OIDCMessageFactory, RegistrationRequest, RegistrationResponse, MessageTuple, OPTIONAL_LOGICAL -from oic.oauth2.message import OPTIONAL_LIST_OF_STRINGS, REQUIRED_LIST_OF_STRINGS, SINGLE_OPTIONAL_STRING, SINGLE_OPTIONAL_INT - - -class MyRegistrationRequest(RegistrationRequest): - c_param = { - "redirect_uris": REQUIRED_LIST_OF_STRINGS, - "response_types": OPTIONAL_LIST_OF_STRINGS, - "grant_types": OPTIONAL_LIST_OF_STRINGS, - "application_type": SINGLE_OPTIONAL_STRING, - "contacts": OPTIONAL_LIST_OF_STRINGS, - "client_name": SINGLE_OPTIONAL_STRING, - "logo_uri": SINGLE_OPTIONAL_STRING, - "client_uri": SINGLE_OPTIONAL_STRING, - "policy_uri": SINGLE_OPTIONAL_STRING, - "tos_uri": SINGLE_OPTIONAL_STRING, - "jwks": SINGLE_OPTIONAL_STRING, - "jwks_uri": SINGLE_OPTIONAL_STRING, - "sector_identifier_uri": SINGLE_OPTIONAL_STRING, - "subject_type": SINGLE_OPTIONAL_STRING, - "id_token_signed_response_alg": SINGLE_OPTIONAL_STRING, - "id_token_encrypted_response_alg": SINGLE_OPTIONAL_STRING, - "id_token_encrypted_response_enc": SINGLE_OPTIONAL_STRING, - "userinfo_signed_response_alg": SINGLE_OPTIONAL_STRING, - "userinfo_encrypted_response_alg": SINGLE_OPTIONAL_STRING, - "userinfo_encrypted_response_enc": SINGLE_OPTIONAL_STRING, - "request_object_signing_alg": SINGLE_OPTIONAL_STRING, - "request_object_encryption_alg": SINGLE_OPTIONAL_STRING, - "request_object_encryption_enc": SINGLE_OPTIONAL_STRING, - "token_endpoint_auth_method": SINGLE_OPTIONAL_STRING, - "token_endpoint_auth_signing_alg": SINGLE_OPTIONAL_STRING, - "default_max_age": SINGLE_OPTIONAL_INT, - "require_auth_time": OPTIONAL_LOGICAL, - "default_acr_values": OPTIONAL_LIST_OF_STRINGS, - "initiate_login_uri": SINGLE_OPTIONAL_STRING, - "request_uris": OPTIONAL_LIST_OF_STRINGS, - "post_logout_redirect_uris": OPTIONAL_LIST_OF_STRINGS, - "frontchannel_logout_uri": SINGLE_OPTIONAL_STRING, - "frontchannel_logout_session_required": OPTIONAL_LOGICAL, - "backchannel_logout_uri": SINGLE_OPTIONAL_STRING, - "backchannel_logout_session_required": OPTIONAL_LOGICAL, - "scope": OPTIONAL_LIST_OF_STRINGS, # added - } - c_default = {"application_type": "web", "response_types": ["code"]} - c_allowed_values = { - "application_type": ["native", "web"], - "subject_type": ["public", "pairwise"], - } - - -class CustomMessageFactory(OIDCMessageFactory): - registration_endpoint = MessageTuple(MyRegistrationRequest, RegistrationResponse) - diff --git a/demos/jans-tent/clientapp/templates/home.html b/demos/jans-tent/clientapp/templates/home.html deleted file mode 100644 index 021c6fcfaac..00000000000 --- a/demos/jans-tent/clientapp/templates/home.html +++ /dev/null @@ -1,21 +0,0 @@ - -
- {{ user|tojson }} --
- {{ id_token|tojson }} -- logout - {% else %} -
A2II;4_xDYID08@H(aYh3Isu1i?w1I~P8whh#(Q=royu<=+YujIS&b_INZ M@dFkB2C7OvX1z|EF=Iz=`mvGmr8 bU2Fm6fNKDcs(dkg$>o=zsqG0D>NG1Zz-f9Cj6~%d=8A3K %Lz1w@>stIzAYt1>&uiYOMZtfK+@M# zzpQKwh$^W|>~7~{FEX6DvZOfADCCOo-fOn4+H}N8GWSDVMnOvwbSEFA(3ustKUCjJ zIvcCY#f`168Xa!^VEO10 +@BTJ7!)bW1w{hDY_L93o`)LvWMesaC>ea zd4Mt?*7J})IRO+8>7>pwOojO6EOpv(e%#`}{kTuR-niL1EFX-hIjO0xu8v!IAi3Yh z$#=XeWE&De;6zUg&L}Cbw>lfRc7$lfFKUE(DGG{lRII3^l3TT~tK}RP9fywd>Cp6* z|Mb@5Yns0D dJ>7ZV}Z&Q>W7zHD%>obPo1eEoL2|j$7q^Fz%^zbKSz!<4@-KDpw{ER7eVtP?U=P zo&fkSg^JH$*e3RgQaPIfuitNKI*N{WLw+^c%FgcUnn`5OVJWPvybu*Nd1AL_)NSC_ zQ!Q*f;!K#+N#;yl&GPUTtvs%qhzj;{L7GAQHO`-+_4klc2oa^U6%NP4(BIl740r5k zzVrNk|4t$ygSfyHYc%(aKOA@{p@88~O;4Ifl+`u_XqPV^1vuls`tCwi%lI+SrCNp_ zAt0bKVluo*)Un=#Sz;_ wa2PohS{*@tK6Q7DLwC-E z2z_y)i5KVYMOj_?>4aN~@2=7=RM@4hP7d?=nAlRZ!@3!avd<_QuOh{r4e@4*61g_1 z01p0E^zjUjff-;Q@7bfrpxp-a%RT+sL(UU@n5E8k?z@ZCR_b>9xDj-3;)*wquCO1{ z-VO9su}Ge-w(seaCuSBF-w^a~v0xVa0N?kYJ`L+Qo9==h4P^L=JjUt4T?62*I4fw; zdr?AAG7Zi?JoGBGShsF_1eLUj2^rE0`gcX 3D{)2bQC!qNK@84R$nd*a z-|K~s$PB0)=WLs>co!yj#? DIfW$2#3YHT`SDcyTZGO+x_wprzw!`seU zI(UHL$1v-OOxH;{b&ANy1*tCf7; TMT88W%hVxc|c*N_A_l`_#>-LvpKhlJ`>%`s10T0C7&7wY` zn7o?{tjL+M<>uTr{Jn#XO;$;Dd0NV?$#au)uU#{8BxQD{nBc~6uPvQ4ErSZYw0zm- zSy`FGUs&8so7S!<_P0Q!uv{Z${5Z}jexCdYM^i+{rYa?F^F^IRjjS^%Dhzx;L>z&~ zfimEe-WoAH}-VdEU*g^71ppI13Ut5|pFhn_jN>X2#6q8HWfczIV)t*7)9M|95=? zr&f<@^Bf%qZ^M=^F&%GZXAdN9*eQu>6 naOhVm}%qH7Xo?)s`b6BkO$Pi&nv z;AV^4_fDLPSUUTDc{!~f>+r+c49Gd=BPKs?X(!^8kvX(`ccEaOIymYKqjq$!{`=9d zyZJ|LFoj2onv5rGVQIOJ;jc*8w8~ym(p=BYB@P!PyZY(VV`qi%J$3<_25uDF7k+oJ z*JSIC%D+;ExG)G>fMsH0oGZ?sTUM<6aY$N~$)16Rq{D!QM90UE<& wl{QT4$;MwNOCI% ~ozv zFIiUp77yGQkJk^_*KiFHK6!3_yyUOBeZGCH8A3bDUymL&3X|eCx4BCN?*ttX1`D*K zOoIR;x$SB(M%PtXwBF`Q%QDXFK MqiWK$_I6^2d+I{lq zF}}{P?Qbp2NkvRES*0s|TVuosa-DBCRSQe0I?Vg@>UC)J1^G5UpM @^xoIP zM+--`j|Cz!EJZ2wIP8pw-KIs}qFdLeq}#NKEu1^ex?f9R4p HI!u~C{i{lbN#3SuCAE`ESNQ<; EHeQYc1KD=FwbmYTeI%$?PuU%i?{y!a(E`wn-sKLSLk&GI7evNV) zd}X^D2gb@C_B&>+?A@}7)qD^nSoo`|zA`h#SOs8Mjw#=?<6;Wzz#ITfG0xqq 6~cRfw5E@Cjrb32Hm;>-&jH%7JlvN93xdrKXk)(-l1!$V+hlw*v>RKdl28 z+?zYb qi|3JFP!R^lS*#4oUq za Nh(j)z_=+-zGh#}#T=1PY!D#XHN7m=2 z544#!+U42dbKS1IavP?lb*ux%zg6e>>B3N5A &nGi_&BT>!3t^9S?(@6AMTE#6rK5JRueDy(IKANp~izhUYz-=hk}f= zcc%^=%4nN-uNZoU7}f51M7(EhCnYk#>vJ|mBjr02FsIg^*M1B$Nm-^W8oL0!zSV3^ z7Y74WQ~7oc&`ZcCVJvdpoa7VfMya38yL^9lrMPp{G3yy6z#T(A&pYxBo%rN!(huQH zQSYB{C`Po&*g~4;pndy*DEh0Z%~)^~MKL_ lv8>1xf)qT|ms9X!9KKHUYn zy!YV2^E7sxD4aTE64AfK#6k!9@t6fKLP<%GDlnbnj2U_Cz5Fwp4m6N_J;MIhQC2xV z3G|IDS4%u`(SrlyZg)!l%174%v3(#%4@G&ww5CqI$DYUTE-Z{eVQW_rPta+Qi0Ny> zDyS{P4liV^5=IdnBgleA^8A|I{_OO^kPK+y=OLdyd=T;>$y_zM$15@ykdLdP4yK81 zWXOOwLLy~{b=)S-6MEFg>2C?ZIuH_KN%Rg9M2bxpH#d1+DnK`sQR@h_;f4|IUCXLw z_}A10(T R2g!$A3{2s)snzz#A29mqDp&bPN_K*AFuro~qb%r{g$kvw z{)V6pfBkVX>mKbs@IT|;$B)%=8%J&mHDo13r=R%$ON)8`i(wd!QKwA>TULBXDH$ni zF?iJD+~J19dWS3;96T;g@A1vZ?3(?v_nb+Zf4}8T^Zdnm{u_0oR8?*AQ??n6_ts3a zxtZj%vXDUA|DJO8ucLGRCR*XA{SWQ$vU<(M95&kWLx4BmonUv6-+70|HG4-3nfqi< zd3=lEpl3nSxY$qxBuBKGbrZfN=kbU4@9oA&_;>~E-1!abS?#lD2pfSt2roO2gMe~f zd2SgI<+o%{ vqg+6OT9-9<_fD623k?9G)PoGTy*+B Gx*^xnm8 zcA{K +}&X(=PWyI zR~UYL9-t7De^JA@j}5!Pun`h+ Td{zq7IpNnfP8H|xWoK7Wl*wz_70@TpF=JM~ z^lsLiIrSWx$qsk&Zr+TeZZVh9uL%qb>n$gz(lsE8W>KnStRl4=@PB!d!#<*#Ad_e| z&pTIU@G|PG`y4DWbjgiisN#2PZ;&kkCaN!Xj Lwo^T38AyhC8Cq)H))E1=biS=a6Rc_MSvFpix?wCcJJjB&4 zaM!N!V!N45Eo-&MTIL>jftUgZscY?AfXP0-F36Fsf^m6LCs<|qCpe(zaF*Yx#T4m} zt0>-^Y|Ll6V|tz@wx0}J? OJl59)e>br}Y5tu{B1-^< k~V(7{E_~g^4m%(8H|9GQ#A_5%q|J~8`{WfHLnhl@Lz%mkd zPcHS#mT#>&+j&JS9Wjy~*5vKEWJ!!uu%%|MUp#A;gn}&v_S9nXY%e{2!+j<8uJCvZ zE#=OUN>qq!1*pIr{m6Z5V)9|aK-L*Ie9KCf0o$RWmMd3k)IIMvh*8ulbBc7+%|eW2 z!}7z(B4U(ToXMxd+H2l{qYHwr=!r1Qx(0j(?7ipCec&jy`#{PSeUuQ6DA~T+mViqI z-}&cOO-fts@38(_T#oDuPcNle8oiltrJ&HC=tk8eW> Cn-Cr*?d{vf;LnRx%2XUEgyFROR7 zn4l<$TbZEZ@ydaq>mF N($e5VCoSZ>wYW&}0 ziHWxh3Uzkvj%1^_2{})3Ua!*e^JJcVB< (_n0t?B19WlF$& zwRw)y!aCSgMmY4|~= RfZE zI%VCfC~>Z+PoFw9mu-a5jd)YRLgu>*O!~sw55{o1gw~;`Ot>U89Lw($TAg`5HUB}< za> {yIp?w!Ih-VEQ&yor#F3HfEKEEe+LmlnPgb3?9_*DNxS%pzTwQA`(m%0y znK)~IlgW1U^GirrL>dMIxj0d0X~+ZTwsXA7b!Hwjp#nGdBx8k7YpMeEU~O6MMR>OA zmC^JIQewaGQZ_^v9FOp+s_C>N32|wF9!Ln)YtkK|sqmGcLrc~xS50*Gci3}ADf~$9 zHo_<$nj|}Jn9#fV>K+;22V|jR76Y)}c$}v}KIOg;Zm^-cNPphEeaRufFABEQGL9)6 z>a1dB!*=^3Ju&3%2$}wd2mr$8nWEmZ3&{Odw*!54UQofwUjX{WEOea}cTbHZ6#wvm zrfGoEj4nz|z16iguV?(10V(n)lb$Xf+H+?PBD2|3w<>wvTu0-5BSys*!Rm`~b13Qh z`=Te?lo5MI_wn|YAxTO_rzF_yWI6lXG62S;l$1ffjL+f} z79^wf_cKC6D_Gk3CJ~LNv4g51@)7oLOM`bCVp!Rw0Zci9w&W1yB=FN!_%yTYt-UF4 zme{xdG=6A$!okpwsL(Dz4a7ygJ&cCu))*syHX05AX3=-irF&yBUoQE8 Y5oB%ivpVONx<4@C;NW&QKw>-@St z60js7&^2_hTU>>tM_pYAf26vT`Wi8)QjmVVA2+MXP;ddwV^}`&4f5ILy&ZjZd5{$< zhDMj4)-eqhDjLCUmdR}QS@4O14VlpQv0=^*4ry2du58-U)1T!F)Z)8~ay!8 V= z>YvZEdfvMM0|%a%&m@~ABz=idrXMrA%DW2w)=#oMrys>&eyC`PWu%gfa`S~Au=xU5 z!`P%#Y%>&%0DT3ACm_f ~`jLzN~+|IC<@`5l5ME(~I{hv=}Fki__BCe`u<8(TU(EWKjzGE2TFC@A5nMYiJM zu_5INRpyxjOZgv?LjJqlh@bXAQP7^vzxen~Vy~9t(-+0-)@|(hX~fxviJSSaA{BuZ z{=ey<1cvvgc*st%eEjAoHc2k0v-$bru%~N)V-uqmgl%$7?r_Yzh?7eQ=nafkw9W$_&Rox;uPJlB5P6+7yu!^b>N-@7%c@mg2a*~p z+bi@RGiD97A>@;CV7zGpC(%ZyH6QEkqvtHMvm5DLp`EPVzrQg0w~s{q%knWphyJg+ z&OfT=I}YQfhPE?rb6wQ5(v4QjW-L;QDYPc-^>xnv+x+1#?m6FkKcDya{eGV3{XB=v&vgEQV+rD7 zaz7ALq4?-^SxQ*)!5fFG!%9cR4|s&Q^%^$vZxHFWsG~c(y4a2P?p#m26vnW?uO(_V z@J`_)2}?82y1Oy66;d{1V aKzCY^Hcc9{BQB0qp4%=++}u-)@sp*8X2IlX z!$~8XNrYx0P-s4 9gQo^Q#QcSUThAzC2;RK{3yvn>{t7A-I!YKrtNS118IX!8&1 zw?Q!JT5UM&2{01OC-;JKG+iU+laftA?OFN;Qq3dG5J8~C!p_xowWB!#1nSN%#xb6s z(!Qd}6S&6D;bl(`@@hd{v&wN>OpMgR&ek@`^pHZ~Wov9`nA@gEijm1`VW+T|yjMBf z1f_Td<8I#KR4AKDR`6*gK@?{iI3eVs*s{=>UH4d*O7ht5Q@)TRJSoF6sXmBzug^uW z0v;wd_FcVtQ&iMkdMO52UM9;Yn?ZDDN-Y%a=PN1}dwAT#JTYXe gop#@)Aov_IbU^8m`Z5DQr<$5jHLsd_WBpXt_HYz zhYM?n`ybpGBuZ3y ?$eK3q|Djz;)GmYP4g8g^yX%|BMg v-Y_Y>!4|&mk`Jbga~s8FTEZ_ zG951O%NM~pZ T6cjWMGr3w^JJQBjW4aLZ@bdsmmlbEOKkmu!qVn{= ze#YCe^Wj4$#9FkA47A@K2tkS+t loe;}vFS0Ces;p01d} nTU* 0nT^LwuWFS8has_Seto>92pGRcgU*R<1ae;OLKD-MI3b~9EcNM zt&~aWUNzkmHBz^$|5X?7nP;x-Ou>4C6C!` p>z2tJ)etqt03MNJwSwChhoYwHwb0Yg%l#nR}<0P z{^bE5jLxk|r{9)!waCa5;DV6?!mrHY>$>^n`J<3a59za}@uO)!^&?H&aU5xrzMnXK z%{7E;9lpMHhf^oc^>;L9YYPtXcF>F`OG12ne85+692D{mUp=blo^o Cg4|bCRSg@8+pV`m zqI@JlObG2`V`lhw?~@*oLi>~U%m#c6>MgFUC0`+oivG~|b~-SD{-o9Jse25UGHq3{ zk}rE%;Y^Q^o;W*`e`_GyC-CMuJ`qxB2@6ngJ1v!+WesLlzR!F=r8}8g4fY6m%bUm~ z+TaE8OtC Xs`BR_Wge#_J6csL$)*y>Mbc# V$=j@=`}m(jJUx~#Dq1Mn{SW6T6ej=x diff --git a/demos/jans-tent/main.py b/demos/jans-tent/main.py deleted file mode 100644 index ebd89f31fd6..00000000000 --- a/demos/jans-tent/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from clientapp import create_app - -if __name__ == '__main__': - app = create_app() - app.debug = True - app.run(host='0.0.0.0', ssl_context=('cert.pem', 'key.pem'), port=9090, use_reloader=False) diff --git a/demos/jans-tent/register_new_client.py b/demos/jans-tent/register_new_client.py deleted file mode 100644 index 89061c42275..00000000000 --- a/demos/jans-tent/register_new_client.py +++ /dev/null @@ -1,12 +0,0 @@ -# executes a new client auto-register from config.py -import logging -from clientapp.utils.dcr_from_config import register - -# add independent logging for CLI script -logging.getLogger('oic') -logging.getLogger('urllib3') -logging.basicConfig( - level=logging.DEBUG, - handlers=[logging.StreamHandler(), logging.FileHandler('register_new_client.log')], - format='[%(asctime)s] %(levelname)s %(name)s in %(module)s : %(message)s') -register() diff --git a/demos/jans-tent/requirements.txt b/demos/jans-tent/requirements.txt deleted file mode 100644 index f6438fdbaae..00000000000 --- a/demos/jans-tent/requirements.txt +++ /dev/null @@ -1,119 +0,0 @@ -appnope==0.1.3 -astroid==2.12.5 -asttokens==2.0.8 -async-generator==1.10 -attrs==22.1.0 -Authlib==1.2.0 -autopep8==1.7.0 -backcall==0.2.0 -bandit==1.7.4 -behave==1.2.6 -certifi==2022.12.7 -cffi==1.15.1 -chardet==5.0.0 -charset-normalizer==2.1.1 -click==8.1.3 -coverage==6.4.4 -cryptography==42.0.0 -decorator==5.1.1 -defusedxml==0.7.1 -dill==0.3.5.1 -dodgy==0.2.1 -EasyProcess==1.1 -executing==1.0.0 -flake8==5.0.4 -Flask==2.2.2 -flask-oidc==1.4.0 -future==0.18.3 -gitdb==4.0.9 -GitPython==3.1.37 -h11==0.13.0 -httplib2==0.21.0 -idna==3.3 -importlib-metadata==4.12.0 -iniconfig==1.1.1 -install==1.3.5 -ipdb==0.13.9 -ipython==8.10.0 -ipython-genutils==0.2.0 -isort==5.10.1 -itsdangerous==2.0.0 -jedi==0.18.1 -Jinja2==3.1.2 -lazy-object-proxy==1.7.1 -Mako==1.2.4 -MarkupSafe==2.1.1 -matplotlib-inline==0.1.6 -mccabe==0.7.0 -more-itertools==8.14.0 -mypy==0.971 -mypy-extensions==0.4.3 -oauth2client==4.1.3 -oic==1.5.0 -outcome==1.2.0 -packaging==21.3 -parse==1.19.0 -parse-type==0.6.0 -parso==0.8.3 -pbr==5.10.0 -pep8==1.7.1 -pep8-naming==0.13.2 -pexpect==4.8.0 -pickleshare==0.7.5 -platformdirs==2.5.2 -pluggy==1.0.0 -poetry-semver==0.1.0 -prompt-toolkit==3.0.31 -prospector==0.12.2 -ptyprocess==0.7.0 -pure-eval==0.2.2 -py==1.11.0 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 -pycodestyle==2.9.1 -pycparser==2.21 -pycryptodomex==3.17 -pydocstyle==6.1.1 -pyflakes==2.5.0 -Pygments==2.13.0 -pyjwkest==1.4.2 -pylama==8.4.1 -pylint==2.15.0 -pylint-celery==0.3 -pylint-common==0.2.5 -pylint-django==2.5.3 -pylint-flask==0.6 -pylint-plugin-utils==0.7 -pyOpenSSL==22.0.0 -pyparsing==3.0.9 -PySocks==1.7.1 -pytest==7.1.3 -python-dotenv==0.21.0 -PyVirtualDisplay==3.0 -PyYAML==6.0 -requests==2.28.1 -requirements-detector==1.0.3 -rsa==4.9 -selenium==4.4.3 -setoptconf==0.3.0 -six==1.16.0 -smmap==5.0.0 -sniffio==1.3.0 -snowballstemmer==2.2.0 -sortedcontainers==2.4.0 -stack-data==0.5.0 -stevedore==4.0.0 -toml==0.10.2 -tomli==2.0.1 -tomlkit==0.11.4 -traitlets==5.3.0 -trio==0.21.0 -trio-websocket==0.9.2 -typed-ast==1.5.4 -typing_extensions==4.3.0 -urllib3==1.26.12 -wcwidth==0.2.5 -Werkzeug==2.2.2 -wrapt==1.14.1 -wsproto==1.2.0 -zipp==3.8.1 diff --git a/demos/jans-tent/tests/behaver/features/environment.py b/demos/jans-tent/tests/behaver/features/environment.py deleted file mode 100644 index 037be43286f..00000000000 --- a/demos/jans-tent/tests/behaver/features/environment.py +++ /dev/null @@ -1,31 +0,0 @@ -from selenium import webdriver -import os -from pyvirtualdisplay import Display - -display = Display(visible=0, size=(1024, 768)) - - -def before_all(context): - os.environ['CURL_CA_BUNDLE'] = "" - display.start() - - -def before_scenario(context, scenario): - options = webdriver.FirefoxOptions() - options.headless = True - context.web = webdriver.Firefox() - - # context.web = webdriver.Firefox() - - -def after_scenario(context, scenario): - context.web.delete_all_cookies() - context.web.close() - - -def after_step(context, step): - print() - - -def after_all(context): - pass diff --git a/demos/jans-tent/tests/behaver/features/oidc_auth.feature b/demos/jans-tent/tests/behaver/features/oidc_auth.feature deleted file mode 100644 index b95f5df3e61..00000000000 --- a/demos/jans-tent/tests/behaver/features/oidc_auth.feature +++ /dev/null @@ -1,40 +0,0 @@ -Feature: Allow authenticated users to access protected pages - - @authenticated - Scenario: User is authenticated - Given username is "johndoe" - And user is authenticated - And protected content link is https://chris.testingenv.org/protected-content - When user clicks the protected content link - Then user access the protected content link - - Scenario: User does not exist - Given user does not exist - And protected content link is https://chris.testingenv.org/protected-content - When user clicks the protected content link - Then user goes to external login page - - Scenario: User is not authenticated - Given username is "johndoe" - And protected content link is https://chris.testingenv.org/protected-content - When user clicks the protected content link - Then user goes to external login page - - # Scenario: Normal user try to access admin content - # Given username is "johndoe" - # And user role is "user" - # And protected content link is https://chris.testingenv.org/admin/admin-protected-content - # When user clicks the protected content link - # Then user gets a 403 error - - # Scenario: Admin can access admin contents - # Given username is "johndoe" - # And user role is "admin" - # And protected content link is https://chris.testingenv.org/admin/admin-protected-content - # When user clicks the protected content link - # Then user access the protected content link - - - - - diff --git a/demos/jans-tent/tests/behaver/features/passport_social_auth.feature b/demos/jans-tent/tests/behaver/features/passport_social_auth.feature deleted file mode 100644 index 685576147f2..00000000000 --- a/demos/jans-tent/tests/behaver/features/passport_social_auth.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: use passport social github to login - """ - As an user, - I want to use passport-social flow to authenticate - So I can access protected-content - """ - - Background: - Given auth method is passport-social - And user is visiting "/" - - Scenario: User is authenticated - Given username is "johndoe" - And protected content link is https://localhost:5000/content/protected-user-content - When user clicks the protected content link - Then user access the protected content link - - Scenario: User is not authenticated - Given user is not authenticated - When user clicks the protected content link - Then user goes to external login page - - - - - \ No newline at end of file diff --git a/demos/jans-tent/tests/behaver/features/steps/allow.py b/demos/jans-tent/tests/behaver/features/steps/allow.py deleted file mode 100644 index 97a6ddfdb4a..00000000000 --- a/demos/jans-tent/tests/behaver/features/steps/allow.py +++ /dev/null @@ -1,116 +0,0 @@ -from behave import when, then, given -import requests -import time -from selenium.webdriver.common.by import By - -base_url = "https://chris.testingenv.org" - - -def cookiesTransformer(sel_session_id, sel_other_cookies): - ''' This transform cookies from selenium to requests ''' - s = requests.Session() - s.cookies.set('session_id', sel_session_id) - i = 0 - while i < len(sel_other_cookies): - s.cookies.set(sel_other_cookies[i]['name'], - sel_other_cookies[i]['value'], - path=sel_other_cookies[i]['path'], - domain=sel_other_cookies[i]['domain'], - secure=sel_other_cookies[i]['secure'], - rest={'httpOnly': sel_other_cookies[i]['httpOnly']}) - i = i + 1 - - return s - - -@given(u'username is "{username}"') -def define_username(context, username): - context.username = username - context.password = "test123" - - -@given(u'user is authenticated') -def user_authenticates(context): - context.web.get("https://chris.testingenv.org/login") - time.sleep(3) - context.web.set_window_size(625, 638) - context.web.find_element(By.ID, "username").click() - context.web.find_element(By.ID, "username").send_keys("johndoo") - time.sleep(3) - context.web.find_element(By.ID, "password").send_keys("test123") - context.web.find_element(By.ID, "loginButton").click() - time.sleep(3) - - -@given(u'protected content link is {protected_content}') -def define_protected_content_link(context, protected_content): - context.protected_content = protected_content - - -@when(u'user clicks the protected content link') -def user_clicks_protected_content_link(context): - - context.web.get(base_url) - time.sleep(2) - context.web.find_element_by_xpath( - '//a[@href="' + "https://chris.testingenv.org/protected-content" + - '"]').click() - context.has_clicked = True - context.response = requests.get(context.protected_content) - - -@then(u'user access the protected content link') -def user_access_protected_content_link(context): - # WE FETCH THE COOKIES FROM SELENIUM AND PASS THEM TO REQUESTS TO VALIDATE - #sel_cookies = context.web.get_cookies() - #sel_cookie = sel_cookies[0] - # set cookie in requests - - # get session id from selenium - #sel_session_id = context.web.session_id - ''' - sess = requests.Session() - - sess.cookies.set('session_id',sel_session_id) - sess.cookies.set( - sel_cookie['name'], - sel_cookie['value'], - path = sel_cookie['path'], - domain = sel_cookie['domain'], - secure = sel_cookie['secure'], - rest= {'httpOnly' : sel_cookie['httpOnly']} - ) - - new_sess = cookiesTransformer(sel_session_id,sel_cookies) - ''' - new_sess = cookiesTransformer(context.web.session_id, - context.web.get_cookies()) - res = new_sess.get(context.protected_content, verify=False) - - assert res.url == context.protected_content - - -@given(u'user does not exist') -def user_does_not_exist(context): - pass - - -@then(u'user goes to external login page') -def user_directed_to_external_login_page(context): - #context.web.get("https://chris.testingenv.org/login") - - time.sleep(1) - external_login_url = 'https://chris.gluutwo.org/oxauth/login.htm' - #import ipdb; ipdb.set_trace() - assert (context.web.current_url == external_login_url) - #new_sess = cookiesTransformer(context.web.session_id,context.web.get_cookies()) - - -@given(u'user role is "{role}"') -def define_user_role(context, role): - context.role = role - - -@then(u'user gets a 403 error') -def step_impl(context): - raise NotImplementedError(u'STEP: Then user gets a 403 error') diff --git a/demos/jans-tent/tests/unit_integration/helper.py b/demos/jans-tent/tests/unit_integration/helper.py deleted file mode 100644 index b282f4b64ab..00000000000 --- a/demos/jans-tent/tests/unit_integration/helper.py +++ /dev/null @@ -1,189 +0,0 @@ - -from unittest import TestCase -from unittest.mock import MagicMock -import clientapp -from clientapp import create_app -from clientapp.helpers.client_handler import ClientHandler -from flask import Flask -from typing import List -import helper -import os -import builtins - - -class FlaskBaseTestCase(TestCase): - def setUp(self): - self.stashed_add_config_from_json = clientapp.add_config_from_json - clientapp.cfg.CLIENT_ID = 'any-client-id-stub' - clientapp.cfg.CLIENT_SECRET = 'any-client-secret-stub' - clientapp.cfg.SERVER_META_URL = 'https://ophostname.com/server/meta/url' - clientapp.cfg.END_SESSION_ENDPOINT = 'https://ophostname.com/end_session_endpoint' - clientapp.add_config_from_json = MagicMock(name='add_config_from_json') - clientapp.add_config_from_json.return_value(None) - self.stashed_discover = ClientHandler.discover - self.stashed_register_client = ClientHandler.register_client - self.stashed_open = builtins.open - builtins.open = MagicMock(name='open') - ClientHandler.discover = MagicMock(name='discover') - ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE - ClientHandler.register_client = MagicMock(name='register_client') - ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE - self.app = create_app() - self.app.testing = True - self.app_context = self.app.test_request_context( - base_url="https://chris.testingenv.org") - self.app_context.push() - self.client = self.app.test_client() - - #self.oauth = OAuth(self.app) - os.environ['AUTHLIB_INSECURE_TRANSPORT'] = "1" - - def tearDown(self) -> None: - ClientHandler.discover = self.stashed_discover - ClientHandler.register_client = self.stashed_register_client - builtins.open = self.stashed_open - clientapp.add_config_from_json = self.stashed_add_config_from_json - - -# Helper functions -def app_endpoints(app: Flask) -> List[str]: - """ Return all enpoints in app """ - endpoints = [] - for item in app.url_map.iter_rules(): - endpoint = item.endpoint.replace("_", "-") - endpoints.append(endpoint) - return endpoints - - -# Mocks -OP_DATA_DICT_RESPONSE = { - 'request_parameter_supported': True, - 'token_revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke', - 'introspection_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/introspection', - 'claims_parameter_supported': False, - 'issuer': 'https://t1.techno24x7.com', - 'userinfo_encryption_enc_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'], - 'id_token_encryption_enc_values_supported': ['A128CBC+HS256', 'A256CBC+HS512', 'A128GCM', 'A256GCM'], - 'authorization_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/authorize', - 'service_documentation': 'http://gluu.org/docs', - 'id_generation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/id', - 'claims_supported': ['street_address', 'country', 'zoneinfo', 'birthdate', 'role', 'gender', 'formatted', - 'user_name', 'phone_mobile_number', 'preferred_username', 'locale', 'inum', 'updated_at', - 'nickname', 'email', 'website', 'email_verified', 'profile', 'locality', - 'phone_number_verified', 'given_name', 'middle_name', 'picture', 'name', 'phone_number', - 'postal_code', 'region', 'family_name'], - 'scope_to_claims_mapping': [{ - 'profile': ['name', 'family_name', 'given_name', 'middle_name', 'nickname', 'preferred_username', 'profile', - 'picture', 'website', 'gender', 'birthdate', 'zoneinfo', 'locale', 'updated_at'] - }, { - 'openid': [] - }, { - 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/scim_access': [] - }, { - 'permission': ['role'] - }, { - 'super_gluu_ro_session': [] - }, { - 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/passport_access': [] - }, { - 'phone': ['phone_number_verified', 'phone_number'] - }, { - 'revoke_session': [] - }, { - 'address': ['formatted', 'postal_code', 'street_address', 'locality', 'country', 'region'] - }, { - 'clientinfo': ['name', 'inum'] - }, { - 'mobile_phone': ['phone_mobile_number'] - }, { - 'email': ['email_verified', 'email'] - }, { - 'user_name': ['user_name'] - }, { - 'oxtrust-api-write': [] - }, { - 'oxd': [] - }, { - 'uma_protection': [] - }, { - 'oxtrust-api-read': [] - }], - 'op_policy_uri': 'http://ox.gluu.org/doku.php?id=oxauth:policy', - 'token_endpoint_auth_methods_supported': ['client_secret_basic', 'client_secret_post', 'client_secret_jwt', - 'private_key_jwt', 'tls_client_auth', 'self_signed_tls_client_auth'], - 'tls_client_certificate_bound_access_tokens': True, - 'response_modes_supported': ['query', 'form_post', 'fragment'], - 'backchannel_logout_session_supported': True, - 'token_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/token', - 'response_types_supported': ['code id_token', 'code', 'id_token', 'token', 'code token', 'code id_token token', - 'id_token token'], - 'request_uri_parameter_supported': True, - 'backchannel_user_code_parameter_supported': False, - 'grant_types_supported': ['implicit', 'refresh_token', 'client_credentials', 'authorization_code', 'password', - 'urn:ietf:params:oauth:grant-type:uma-ticket'], - 'ui_locales_supported': ['en', 'bg', 'de', 'es', 'fr', 'it', 'ru', 'tr'], - 'userinfo_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/userinfo', - 'op_tos_uri': 'http://ox.gluu.org/doku.php?id=oxauth:tos', - 'auth_level_mapping': { - '-1': ['simple_password_auth'], - '60': ['passport_saml'], - '40': ['passport_social'] - }, - 'require_request_uri_registration': False, - 'id_token_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'], - 'frontchannel_logout_session_supported': True, - 'claims_locales_supported': ['en'], - 'clientinfo_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/clientinfo', - 'request_object_signing_alg_values_supported': ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', - 'ES256', 'ES384', 'ES512'], - 'request_object_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'], - 'session_revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke_session', - 'check_session_iframe': 'https://t1.techno24x7.com/oxauth/opiframe.htm', - 'scopes_supported': ['address', 'openid', 'clientinfo', 'user_name', 'profile', - 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/scim_access', 'uma_protection', - 'permission', 'revoke_session', 'oxtrust-api-write', 'oxtrust-api-read', 'phone', - 'mobile_phone', 'oxd', 'super_gluu_ro_session', 'email', - 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/passport_access'], - 'backchannel_logout_supported': True, - 'acr_values_supported': ['simple_password_auth', 'passport_saml', 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', 'passport_social'], - 'request_object_encryption_enc_values_supported': ['A128CBC+HS256', 'A256CBC+HS512', 'A128GCM', 'A256GCM'], - 'display_values_supported': ['page', 'popup'], - 'userinfo_signing_alg_values_supported': ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', - 'ES512'], - 'claim_types_supported': ['normal'], - 'userinfo_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'], - 'end_session_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/end_session', - 'revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke', - 'backchannel_authentication_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/bc-authorize', - 'token_endpoint_auth_signing_alg_values_supported': ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', - 'ES384', 'ES512'], - 'frontchannel_logout_supported': True, - 'jwks_uri': 'https://t1.techno24x7.com/oxauth/restv1/jwks', - 'subject_types_supported': ['public', 'pairwise'], - 'id_token_signing_alg_values_supported': ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', - 'ES384', 'ES512'], - 'registration_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/register', - 'id_token_token_binding_cnf_values_supported': ['tbh'] -} - -REGISTER_CLIENT_RESPONSE = {'allow_spontaneous_scopes': False, 'application_type': 'web', 'rpt_as_jwt': False, - 'registration_client_uri': 'https://t1.techno24x7.com/jans-auth/restv1/register?client_id' - '=079f3682-3d60-4bca-8ff7-bbc7dbc18cd7', - 'run_introspection_script_before_jwt_creation': False, - 'registration_access_token': '89c51fd6-34ec-497e-a4ae-85e21b7e725b', - 'client_id': '079f3682-3d60-4bca-8ff7-bbc7dbc18cd7', - 'token_endpoint_auth_method': 'client_secret_post', - 'scope': 'online_access device_sso openid permission uma_protection offline_access', - 'client_secret': '8f53c454-f6ee-4181-8581-9f1ee120b878', 'client_id_issued_at': 1680038429, - 'backchannel_logout_session_required': False, 'client_name': 'Jans Tent', - 'par_lifetime': 600, 'spontaneous_scopes': [], 'id_token_signed_response_alg': 'RS256', - 'access_token_as_jwt': False, 'grant_types': ['authorization_code'], - 'subject_type': 'pairwise', 'additional_token_endpoint_auth_methods': [], - 'keep_client_authorization_after_expiration': False, 'require_par': False, - 'redirect_uris': ['https://localhost:9090/oidc_callback'], 'additional_audience': [], - 'frontchannel_logout_session_required': False, 'client_secret_expires_at': 0, - 'access_token_signing_alg': 'RS256', 'response_types': ['code']} - - diff --git a/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py b/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py deleted file mode 100644 index 91ebb43edca..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py +++ /dev/null @@ -1,62 +0,0 @@ -import clientapp -from flask import Flask, url_for -from typing import List -from helper import FlaskBaseTestCase - - -def app_endpoints(app: Flask) -> List[str]: - """ Return all enpoints in app """ - - endpoints = [] - for item in app.url_map.iter_rules(): - endpoint = item.endpoint.replace("_", "-") - endpoints.append(endpoint) - return endpoints - - -# class FlaskBaseTestCase(TestCase): -# def setUp(self): -# self.app = clientapp.create_app() -# self.app.testing = True -# self.app_context = self.app.test_request_context( -# base_url="https://chris.testingenv.org") -# self.app_context.push() -# self.client = self.app.test_client() -# #self.oauth = OAuth(self.app) -# os.environ['AUTHLIB_INSECURE_TRANSPORT'] = "1" - - -class TestCallbackEndpoint(FlaskBaseTestCase): - def test_oidc_callback_endpoint_exist(self): - endpoints = [] - for item in clientapp.create_app().url_map.iter_rules(): - endpoint = item.rule - endpoints.append(endpoint) - - self.assertTrue('/oidc_callback' in endpoints, - "enpoint /oidc_callback knão existe no app") - - def test_callback_endpoint_should_exist(self): - - self.assertTrue('callback' in app_endpoints(clientapp.create_app()), - 'endpoint /callback does not exist in app') - - def test_endpoint_args_without_code_should_return_400(self): - resp = self.client.get(url_for('callback')) - - self.assertEqual(resp.status_code, 400) - - -''' - def test_endpoint_should_return_status_code_302(self): - # if there is - - self.assertEqual( - self.client.get(url_for('callback')).status_code, - 302, - 'Callback endpoint is not returning 302 status_code' - ) - - - #def test_endpoint_should_return_ -''' diff --git a/demos/jans-tent/tests/unit_integration/test_cfg_checker.py b/demos/jans-tent/tests/unit_integration/test_cfg_checker.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py b/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py deleted file mode 100644 index 83aef096738..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py +++ /dev/null @@ -1,145 +0,0 @@ -from helper import FlaskBaseTestCase -import clientapp -import helper -from flask import url_for -from clientapp.helpers.client_handler import ClientHandler -from unittest.mock import MagicMock, patch - - -class TestRegisterEndpoint(FlaskBaseTestCase): - - def test_if_app_has_register_endpoint(self): - self.assertIn( - 'register', - helper.app_endpoints(clientapp.create_app()) - ) - - def test_if_endpoint_accepts_post(self): - methods = None - for rule in self.app.url_map.iter_rules('register'): - methods = rule.methods - self.assertIn( - 'POST', - methods - ) - - # def test_init_should_call_discover_once(self): - # ClientHandler.discover = MagicMock(name='discover') - # ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE - # ClientHandler.discover.assert_called_once() - - def test_endpoint_should_return_valid_req(self): - self.assertIn( - self.client.post(url_for('register')).status_code, - range(100, 511), - '/register returned invalid requisition' - ) - - @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None)) - def test_endpoint_should_init_client_handler(self): - self.client.post(url_for('register'), json={ - 'op_url': 'https://test.com', - 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback'] - }) - ClientHandler.__init__.assert_called_once() - - @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None)) - def test_endpoint_should_accept_2_params(self): - first_value = 'https://op' - second_value = ['https://client.com.br/oidc_callback'] - - self.client.post(url_for('register'), json={ - 'op_url': first_value, - 'redirect_uris': second_value - }) - ClientHandler.__init__.assert_called_once_with(first_value, second_value, {}) - - @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None)) - def test_endpoint_should_accept_3_params(self): - first_value = 'https://op' - second_value = ['https://client.com.br/oidc_callback'] - third_value = {'scope': 'openid email profile'} - - self.client.post(url_for('register'), json={ - 'op_url': first_value, - 'redirect_uris': second_value, - 'additional_params': third_value - }) - - ClientHandler.__init__.assert_called_once_with(first_value, second_value, third_value) - - def test_endpoint_should_return_error_code_400_if_no_data_sent(self): - self.assertEqual( - self.client.post(url_for('register')).status_code, - 400, - 'status_code for empty request is NOT 400' - ) - - def test_should_return_400_error_if_no_needed_keys_provided(self): - self.assertEqual( - self.client.post(url_for('register'), json={ - 'other_key': 'othervalue', - 'another_key': 'another_value' - }).status_code, - 400, - 'not returning 400 code if no needed keys provided' - ) - - def test_should_return_400_if_values_are_not_valid_urls(self): - self.assertEqual( - self.client.post(url_for('register'), json={ - 'op_url': 'not_valid_url', - 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback'] - }).status_code, - 400, - 'not returning status 400 if values are not valid urls' - ) - - @patch('clientapp.helpers.client_handler.ClientHandler.get_client_dict', MagicMock(return_value=None)) - def test_valid_post_should_should_call_get_client_dict_once(self): - op_url = 'https://op.com.br' - self.client.post(url_for('register'), json={ - 'op_url': op_url, - 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback'] - }) - ClientHandler.get_client_dict.assert_called_once() - - def test_should_should_return_200_if_registered(self): - op_url = 'https://op.com.br' - test_client_id = '1234-5678-9ten11' - test_client_secret = 'mysuperprotectedsecret' - with patch.object(ClientHandler, 'get_client_dict', return_value={ - 'op_metadata_url': '%s/.well-known/open-id-configuration' % op_url, - 'client_id': test_client_id, - 'client_secret': test_client_secret - }) as get_client_dict: - response = self.client.post(url_for('register'), json={ - 'op_url': op_url, - 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback'] - }) - self.assertEqual(response.status_code, 200) - get_client_dict.reset() - - def test_should_return_expected_keys(self): - op_url = 'https://op.com.br' - redirect_uris = ['https://client.com.br/oidc_calback'] - test_client_id = '1234-5678-9ten11' - test_client_secret = 'mysuperprotectedsecret' - additional_params = {'param1': 'value1'} - - expected_keys = {'op_metadata_url', 'client_id', 'client_secret'} - - with patch.object(ClientHandler, 'get_client_dict', return_value={ - 'op_metadata_url': '%s/.well-known/open-id-configuration' % op_url, - 'client_id': test_client_id, - 'client_secret': test_client_secret - }) as get_client_dict: - response = self.client.post(url_for('register'), json={ - 'op_url': op_url, - 'redirect_uris': redirect_uris, - 'additional_params': additional_params - }) - print(response) - assert expected_keys <= response.json.keys(), response.json - - get_client_dict.reset() diff --git a/demos/jans-tent/tests/unit_integration/test_config.py b/demos/jans-tent/tests/unit_integration/test_config.py deleted file mode 100644 index 419e3bd56a9..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_config.py +++ /dev/null @@ -1,19 +0,0 @@ -import clientapp.config as cfg -from unittest import TestCase - - -class TestConfig(TestCase): - def test_has_attribute_SSL_VERIFY(self): - self.assertTrue(hasattr(cfg, 'SSL_VERIFY'), - 'SSL_VERIFY attribute is missing in config.') - - def test_SSL_VERIFY_has_boolean_value(self): - self.assertTrue('__bool__' in cfg.SSL_VERIFY.__dir__(), - 'SSL_VERIFY is not boolean.') - - def test_has_attribute_SCOPE(self): - self.assertTrue(hasattr(cfg, 'SCOPE'), - 'SCOPE attribute is missing in config.') - - def test_SCOPE_default_should_be_openid(self): - self.assertTrue(cfg.SCOPE == 'openid') diff --git a/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py b/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py deleted file mode 100644 index 1890b1d4cee..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py +++ /dev/null @@ -1,107 +0,0 @@ -import clientapp -from flask import Flask, url_for -from typing import List -import json -from helper import FlaskBaseTestCase - - -def app_endpoints(app: Flask) -> List[str]: - """ Return all enpoints in app """ - - endpoints = [] - for item in app.url_map.iter_rules(): - endpoint = item.endpoint.replace("_", "-") - endpoints.append(endpoint) - return endpoints - - -def valid_client_configuration(): - return { - "client_id": "my-client-id", - "client_secret": "my-client-secret", - "op_metadata_url": "https://op.com/.well-known/openidconfiguration" - } - - -class TestConfigurationEndpoint(FlaskBaseTestCase): - def test_create_app_has_configuration(self): - self.assertTrue( - 'configuration' in app_endpoints(clientapp.create_app()), - 'endpoint /configuration does not exist in app') - - def test_configuration_endpoint_should_return_valid_req(self): - self.assertIn( - self.client.post(url_for('configuration')).status_code, - range(100, 511), '/configuration returned invalid requisition') - - def test_endpoint_should_return_200_if_valid_json(self): - headers = {'Content-type': 'application/json'} - data = {'provider_id': 'whatever'} - json_data = json.dumps(data) - response = self.client.post(url_for('configuration'), - data=json_data, - headers=headers) - self.assertEqual(response.status_code, 200) - - def test_endpoint_should_return_posted_data_if_valid_json(self): - headers = {'Content-type': 'application/json'} - data = {'provider_id': 'whatever'} - json_data = json.dumps(data) - response = self.client.post(url_for('configuration'), - data=json_data, - headers=headers) - - self.assertEqual(json_data, json.dumps(response.json)) - - def test_endpoint_should_setup_cfg_with_provider_id(self): - headers = {'Content-type': 'application/json'} - data = {'provider_id': 'whatever'} - json_data = json.dumps(data) - self.client.post(url_for('configuration'), - data=json_data, - headers=headers) - - self.assertEqual(clientapp.cfg.PRE_SELECTED_PROVIDER_ID, 'whatever') - - def test_endpoint_should_setup_cfg_with_pre_selected_provider_true(self): - clientapp.cfg.PRE_SELECTED_PROVIDER = False - headers = {'Content-type': 'application/json'} - data = {'provider_id': 'whatever'} - json_data = json.dumps(data) - self.client.post(url_for('configuration'), - data=json_data, - headers=headers) - - self.assertTrue(clientapp.cfg.PRE_SELECTED_PROVIDER, ) - - def test_endpoint_should_return_200_if_valid_client_config(self): - headers = {'Content-type': 'application/json'} - json_data = json.dumps(valid_client_configuration()) - response = self.client.post( - url_for('configuration'), data=json_data, headers=headers) - self.assertEqual(response.status_code, 200, - 'endpoint is NOT returning 200 for valid client configuration') - - def test_endpoint_should_register_new_oauth_client_id(self): - headers = {'Content-type': 'application/json'} - client_id = "my-client-id" - client_secret = "my-client-secret" - op_metadata_url = "https://op.com/.well-known/openidconfiguration" - json_data = json.dumps({ - "client_id": client_id, - "client_secret": client_secret, - "op_metadata_url": op_metadata_url - }) - self.client.post( - url_for('configuration'), data=json_data, headers=headers) - self.assertTrue(clientapp.oauth.op.client_id == client_id, - 'endpoint is NOT changing op.client_id') - - def test_endpoint_should_register_new_oauth_client_secret(self): - headers = {'Content-type': 'application/json'} - json_data = json.dumps(valid_client_configuration()) - client_secret = valid_client_configuration()['client_secret'] - self.client.post( - url_for('configuration'), data=json_data, headers=headers) - self.assertTrue(clientapp.oauth.op.client_secret == client_secret, - '%s is is not %s' % (clientapp.oauth.op.client_secret, client_secret)) diff --git a/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py b/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py deleted file mode 100644 index e02f909ea28..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py +++ /dev/null @@ -1,76 +0,0 @@ -from clientapp.utils import dcr_from_config -from clientapp import config as cfg -from unittest.mock import MagicMock, patch, mock_open -from unittest import TestCase -from clientapp.helpers.client_handler import ClientHandler -import helper -import json -import builtins - -class TestDrcFromConfig(TestCase): - - def setUp(self) -> None: - # stashing to restore on teardown - self.stashed_discover = ClientHandler.discover - self.stashed_register_client = ClientHandler.register_client - self.stashed_open = builtins.open - ClientHandler.discover = MagicMock(name='discover') - ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE - ClientHandler.register_client = MagicMock(name='register_client') - ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE - builtins.open = MagicMock(name='open') - - def tearDown(self) -> None: - ClientHandler.discover = self.stashed_discover - ClientHandler.register_client = self.stashed_register_client - builtins.open = self.stashed_open - - def test_if_setup_logging_exists(self): - assert hasattr(dcr_from_config, 'setup_logging') - - def test_if_static_variables_exists(self): - assert hasattr(dcr_from_config, 'OP_URL') - assert hasattr(dcr_from_config, 'REDIRECT_URIS') - - def test_if_static_variables_from_config(self): - assert dcr_from_config.OP_URL == cfg.ISSUER - assert dcr_from_config.REDIRECT_URIS == cfg.REDIRECT_URIS - - def test_register_should_be_calable(self): - assert callable(dcr_from_config.register), 'not callable' - - @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None)) - def test_register_should_call_ClientHandler(self): - dcr_from_config.register() - ClientHandler.__init__.assert_called_once() - - @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None)) - def test_register_should_call_ClientHandler_with_params(self): - dcr_from_config.register() - ClientHandler.__init__.assert_called_once_with( - cfg.ISSUER, cfg.REDIRECT_URIS, { - 'scope': cfg.SCOPE.split(" "), - "post_logout_redirect_uris": ['https://localhost:9090'] - } - ) - - def test_register_should_call_open(self): - with patch('builtins.open', mock_open()) as open_mock: - dcr_from_config.register() - - open_mock.assert_called_once() - - def test_register_should_call_open_with_correct_params(self): - with patch('builtins.open', mock_open()) as open_mock: - dcr_from_config.register() - open_mock.assert_called_once_with('client_info.json', 'w') - - def test_register_should_call_write_with_client_info(self): - client = ClientHandler(cfg.ISSUER, cfg.REDIRECT_URIS, {}) - expected_json_client_info = json.dumps(client.get_client_dict(), indent=4) - with patch('builtins.open', mock_open()) as open_mock: - dcr_from_config.register() - open_mock_handler = open_mock() - open_mock_handler.write.assert_called_once_with(expected_json_client_info) - - diff --git a/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py b/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py deleted file mode 100644 index 0f51b7cb596..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py +++ /dev/null @@ -1,277 +0,0 @@ -from unittest import TestCase -from unittest.mock import MagicMock -import inspect - -import clientapp.helpers.client_handler as client_handler -from typing import Optional -import helper -from oic.oauth2 import ASConfigurationResponse - - -ClientHandler = client_handler.ClientHandler - -# helper -def get_class_instance(op_url='https://t1.techno24x7.com', - client_url='https://mock.test.com', - additional_metadata={}): - client_handler_obj = ClientHandler(op_url, client_url, additional_metadata) - return client_handler_obj - - -class TestDynamicClientRegistration(TestCase): - - def setUp(self) -> None: - self.register_client_stash = ClientHandler.register_client - self.discover_stash = ClientHandler.discover - - @staticmethod - def mock_methods(): - ClientHandler.register_client = MagicMock(name='register_client') - ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE - ClientHandler.discover = MagicMock(name='discover') - ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE - - def restore_stashed_mocks(self): - ClientHandler.discover = self.discover_stash - ClientHandler.register_client = self.register_client_stash - - def test_if_json_exists(self): - self.assertTrue(hasattr(client_handler, 'json'), - 'json does not exists in client_handler') - - def test_if_json_is_from_json_package(self): - self.assertTrue(client_handler.json.__package__ == 'json', - 'json is not from json') - - # testing ClientHandler class - def test_if_ClientHandler_is_class(self): - self.assertTrue(inspect.isclass(ClientHandler)) - - def test_if_register_client_exists(self): - self.assertTrue(hasattr(ClientHandler, 'register_client'), - 'register_client does not exists in ClientHandler') - - def test_if_register_client_is_callable(self): - self.assertTrue(callable(ClientHandler.register_client), - 'register_client is not callable') - - def test_if_register_client_receives_params(self): - expected_args = ['self', 'op_data', 'redirect_uris'] - self.assertTrue( - inspect.getfullargspec( - ClientHandler.register_client).args == expected_args, - 'register_client does not receive expected args') - - def test_if_register_client_params_are_expected_type(self): - insp = inspect.getfullargspec(ClientHandler.register_client) - self.assertTrue( - insp.annotations['op_data'] == ASConfigurationResponse - and insp.annotations['redirect_uris'] == Optional[list[str]], - 'register_client is not receiving the right params') - - def test_if_class_has_initial_expected_attrs(self): - initial_expected_attrs = [ - '_ClientHandler__client_id', - '_ClientHandler__client_secret', - '_ClientHandler__redirect_uris', - '_ClientHandler__metadata_url', - '_ClientHandler__additional_metadata', - 'discover', # method - 'register_client' # method - ] - - self.assertTrue( - all(attr in ClientHandler.__dict__.keys() - for attr in initial_expected_attrs), - 'ClientHandler does not have initial attrs') - - def test_if_discover_exists(self): - self.assertTrue(hasattr(ClientHandler, 'discover'), - 'discover does not exists in ClientHandler') - - def test_if_discover_is_callable(self): - self.assertTrue(callable(ClientHandler.discover), - 'discover is not callable') - - def test_if_discover_receives_params(self): - expected_args = ['self', 'op_url'] - self.assertTrue( - inspect.getfullargspec( - ClientHandler.discover).args == expected_args, - 'discover does not receive expected args') - - def test_if_discover_params_are_expected_type(self): - insp = inspect.getfullargspec(ClientHandler.discover) - self.assertTrue( - insp.annotations['op_url'] == Optional[str], - 'discover is not receiving the right params') - - def test_discover_should_return_valid_dict(self): - """[Checks if returns main keys] - """ - - main_keys = { - 'issuer', 'authorization_endpoint', 'token_endpoint', - 'userinfo_endpoint', 'clientinfo_endpoint', - 'session_revocation_endpoint', 'end_session_endpoint', - 'revocation_endpoint', 'registration_endpoint' - } - - self.mock_methods() - op_data = ClientHandler.discover(ClientHandler, - 'https://t1.techno24x7.com') - self.assertTrue(main_keys <= set(op_data), - 'discovery return data does not have main keys') - self.restore_stashed_mocks() - - def test_if_get_client_dict_exists(self): - self.assertTrue(hasattr(ClientHandler, 'get_client_dict'), - 'get_client_dict does not exists in ClientHandler') - - def test_if_get_client_dict_is_callable(self): - self.assertTrue(callable(ClientHandler.get_client_dict), - 'get_client_dict is not callable') - - def test_if_get_client_dict_receives_params(self): - expected_args = ['self'] - self.assertTrue( - inspect.getfullargspec( - ClientHandler.get_client_dict).args == expected_args, - 'get_client_dict does not receive expected args') - - def test_client_id_should_return_something(self): - self.assertIsNotNone( - ClientHandler.get_client_dict(ClientHandler), - 'get_client_dict returning NoneType. It has to return something!') - - def test_get_client_dict_should_return_a_dict(self): - self.assertIsInstance(ClientHandler.get_client_dict(ClientHandler), - dict, 'get_client_dict is not returning a dict') - - def test_class_init_should_set_op_url(self): - self.mock_methods() - - op_url = 'https://t1.techno24x7.com' - - client_handler_obj = get_class_instance(op_url) - - self.restore_stashed_mocks() - - self.assertEqual(client_handler_obj.__dict__['_ClientHandler__op_url'], - op_url) - - def test_class_init_should_set_redirect_uris(self): - self.mock_methods() - - op_url = 'https://t1.techno24x7.com' - redirect_uris = 'https://mock.test.com/oidc_callback' - client_handler_obj = ClientHandler(op_url, redirect_uris, {}) - - self.restore_stashed_mocks() - - self.assertEqual( - client_handler_obj.__dict__['_ClientHandler__redirect_uris'], - redirect_uris) - - def test_class_init_should_set_metadata_url(self): - self.mock_methods() - - op_url = 'https://t1.techno24x7.com' - - client_handler_obj = get_class_instance(op_url) - - self.restore_stashed_mocks() - - expected_metadata_url = op_url + '/.well-known/openid-configuration' - - self.assertEqual( - client_handler_obj.__dict__['_ClientHandler__metadata_url'], - expected_metadata_url) - - def test_class_init_should_set_additional_params(self): - self.mock_methods() - expected_metadata = {'metakey1': 'meta value 1'} - client_handler_obj = get_class_instance(additional_metadata=expected_metadata) - self.restore_stashed_mocks() - - self.assertEqual( - client_handler_obj.__dict__['_ClientHandler__additional_metadata'], - expected_metadata - ) - - def test_class_init_should_have_docstring(self): - self.assertTrue(ClientHandler.__init__.__doc__, - 'ClientHandler.__init__ has doc') - - def test_if_get_client_dict_return_expected_keys(self): - expected_keys = [ - 'op_metadata_url', - 'client_id', - 'client_secret', - ] - - self.mock_methods() - - client_handler_obj = get_class_instance() - client_dict = client_handler_obj.get_client_dict() - - self.restore_stashed_mocks() - - self.assertTrue( - all(key in client_dict.keys() for key in expected_keys), - 'there is no %s IN %s: get_client_dict is NOT returning expected keys' - % (str(expected_keys), str(client_dict.keys()))) - - def test_get_client_dict_values_cannot_be_none(self): - self.mock_methods() - - op_url = 'https://t1.techno24x7.com' - client_handler_obj = get_class_instance(op_url) - client_dict = client_handler_obj.get_client_dict() - - self.restore_stashed_mocks() - - for key in client_dict.keys(): - self.assertIsNotNone(client_dict[key], - 'get_client_dict[%s] cannot be None!' % key) - - def test_get_client_dict_should_return_url_metadata_value(self): - self.mock_methods() - - client_handler_obj = get_class_instance() - - self.restore_stashed_mocks() - - self.assertEqual( - client_handler_obj.get_client_dict()['op_metadata_url'], - client_handler_obj._ClientHandler__metadata_url) - - def test_get_client_dict_should_return_client_id_value(self): - self.mock_methods() - - client_handler_obj = get_class_instance() - - self.restore_stashed_mocks() - - self.assertEqual( - client_handler_obj.get_client_dict()['client_id'], - client_handler_obj._ClientHandler__client_id - ) - - def test_init_should_call_discover_once(self): - self.mock_methods() - - get_class_instance() - - ClientHandler.discover.assert_called_once() - - self.restore_stashed_mocks() - - def test_init_should_call_register_client_once(self): - self.mock_methods() - - get_class_instance() - ClientHandler.register_client.assert_called_once() - - self.restore_stashed_mocks() - diff --git a/demos/jans-tent/tests/unit_integration/test_flask_factory.py b/demos/jans-tent/tests/unit_integration/test_flask_factory.py deleted file mode 100644 index 0813c3b1ad1..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_flask_factory.py +++ /dev/null @@ -1,93 +0,0 @@ -from unittest import TestCase -from unittest.mock import MagicMock -import clientapp -from flask import Flask -import os -import builtins -from clientapp.helpers.client_handler import ClientHandler -import helper - - -class TestFlaskApp(TestCase): - - def setUp(self) -> None: - self.stashed_add_config_from_json = clientapp.add_config_from_json - clientapp.cfg.CLIENT_ID = 'any-client-id-stub' - clientapp.cfg.CLIENT_SECRET = 'any-client-secret-stub' - clientapp.cfg.SERVER_META_URL = 'https://ophostname.com/server/meta/url' - clientapp.add_config_from_json = MagicMock(name='add_config_from_json') - clientapp.add_config_from_json.return_value(None) - self.stashed_discover = ClientHandler.discover - self.stashed_register_client = ClientHandler.register_client - self.stashed_open = builtins.open - builtins.open = MagicMock(name='open') - ClientHandler.discover = MagicMock(name='discover') - ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE - ClientHandler.register_client = MagicMock(name='register_client') - ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE - - def tearDown(self) -> None: - ClientHandler.discover = self.stashed_discover - ClientHandler.register_client = self.stashed_register_client - builtins.open = self.stashed_open - clientapp.add_config_from_json = self.stashed_add_config_from_json - - def test_create_app_should_exist(self): - self.assertEqual(hasattr(clientapp, 'create_app'), True, - 'app factory does not exists') - - def test_create_app_should_be_invokable(self): - self.assertEqual(callable(clientapp.create_app), True, - 'cannot invoke create_app from clientapp') - - def test_create_app_should_return_a_flask_app(self): - - self.assertIsInstance(clientapp.create_app(), Flask, - 'create_app is not returning a Flask instance') - - def test_if_app_has_secret_key(self): - self.assertTrue(hasattr(clientapp.create_app(), 'secret_key'), ) - - def test_if_secret_key_not_none(self): - self.assertIsNotNone(clientapp.create_app().secret_key, - 'app secret key is unexpectedly None') - - def test_if_oauth_is_app_extension(self): - self.assertTrue('authlib.integrations.flask_client' in - clientapp.create_app().extensions) - - def test_if_settings_py_exists(self): - self.assertTrue(os.path.exists('clientapp/config.py'), - 'File clientapp/config.py does not exist') - - def test_if_op_client_id_exists_in_app_configuration(self): - self.assertTrue('OP_CLIENT_ID' in clientapp.create_app().config, - 'No OP_CLIENT_ID in app.config') - - def test_if_clientapp_has_cfg(self): - self.assertTrue(hasattr(clientapp, 'cfg')) - - def test_if_cfg_is_module_from_configpy(self): - self.assertTrue( - os.path.relpath(clientapp.cfg.__file__) == 'clientapp/config.py') - - ... - - def test_if_OP_CLIENT_ID_is_equal_cfg_CLIENT_ID(self): - self.assertEqual(clientapp.create_app().config['OP_CLIENT_ID'], - clientapp.cfg.CLIENT_ID) - - def test_if_OP_CLIENT_SECRET_exists_in_app_configuration(self): - self.assertTrue('OP_CLIENT_SECRET' in clientapp.create_app().config, - 'No OP_CLIENT_SECRET in app.config') - - def test_if_OP_CLIENT_SECRET_is_equal_cfg_CLIENT_ID(self): - self.assertEqual(clientapp.create_app().config['OP_CLIENT_SECRET'], - clientapp.cfg.CLIENT_SECRET) - - def test_if_has_attr_ssl_verify(self): - self.assertTrue(hasattr(clientapp, 'ssl_verify'), - 'There is no ssl_verify in clientapp') - - def test_should_have_method_to_set_CA_CURL_CERT(self): - self.assertTrue(clientapp.ssl_verify.__call__) diff --git a/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py b/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py deleted file mode 100644 index 4f7fc9dc13f..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py +++ /dev/null @@ -1,46 +0,0 @@ -import clientapp -from helper import FlaskBaseTestCase - - -class TestPreselectedProvider(FlaskBaseTestCase): - - # """ - # We should be able to send Preselected passport provider to gluu OIDC as a authorization param - # like this: preselectedExternalProvider= - # Where is the Base64-encoded representation of a small JSON - # content that looking like this: - # { "provider" : } - # """ - def setUp(self): - clientapp.cfg.PRE_SELECTED_PROVIDER = True - FlaskBaseTestCase.setUp(FlaskBaseTestCase) - - def test_config_should_have_preselected_provider_option(self): - self.assertTrue(hasattr(clientapp.cfg, 'PRE_SELECTED_PROVIDER'), - 'cfg doesnt have PRE_SELECTED_PROVIDER attribute') - - def test_config_pre_selected_provider_should_be_boolean(self): - self.assertTrue( - type(clientapp.cfg.PRE_SELECTED_PROVIDER) == bool, - 'cfg.PRE_SELECTED_PROVIDER is not bool') - - def test_preselected_provider_id_should_exist_in_cfg(self): - self.assertTrue(hasattr(clientapp.cfg, 'PRE_SELECTED_PROVIDER_ID')) - - def test_clientapp_should_have_get_preselected_provider(self): - self.assertTrue( - hasattr(clientapp, 'get_preselected_provider'), - 'client app does not have get_preselected_provider attr') - - def test_get_preselected_provider_should_be_callable(self): - self.assertTrue(callable(clientapp.get_preselected_provider), - 'get_preselected_provider is not callable') - - def test_get_selected_provider_should_return_base64(self): - - clientapp.cfg.PRE_SELECTED_PROVIDER_ID = 'saml-emaillink' - expected_response = "eyAicHJvdmlkZXIiIDogInNhbWwtZW1haWxsaW5rIiB9" - self.assertEqual(clientapp.get_preselected_provider(), - expected_response) - - diff --git a/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py b/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py deleted file mode 100644 index c0bb85bfc87..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py +++ /dev/null @@ -1,65 +0,0 @@ -import clientapp -from helper import FlaskBaseTestCase, app_endpoints -from flask import url_for, session -from urllib import parse -from clientapp import config as cfg - -class TestLogoutEndpoint(FlaskBaseTestCase): - def authenticated_session_mock(self): - with self.client.session_transaction() as session: - session['id_token'] = 'id_token_stub' - - def test_endpoint_exists(self): - self.assertIn( - 'logout', - app_endpoints(clientapp.create_app()) - ) - - def test_endpoint_should_require_authentication(self): - ... - def test_logout_endpoint_should_redirect_to_home_if_unauthenticated(self): - # print(self.client.get(url_for('logout')).response) - response = self.client.get(url_for('logout')) - assert(response.status_code == 302) - assert(response.location == url_for('index')) - - - def test_logout_endpoint_should_clear_session(self): - with self.client.session_transaction() as sess: - sess['id_token'] = 'id_token_stub' - sess['user'] = 'userinfo stub' - - with self.client: - self.client.get(url_for('logout')) - assert 'id_token' not in session - assert 'user' not in session - - def test_endpoint_should_redirect_to_end_session_endpoint(self): - with self.client.session_transaction() as session: - session['id_token'] = 'id_token_stub' - session['user'] = 'userinfo stub' - - response = self.client.get(url_for('logout')) - - parsed_location = parse.urlparse(response.location) - assert parsed_location.scheme == 'https' - assert parsed_location.netloc == 'ophostname.com' - assert parsed_location.path == '/end_session_endpoint' - - - - def test_endpoint_should_redirect_to_end_session_endpoint_with_params(self): - token_stub = 'id_token_stub' - with self.client.session_transaction() as session: - session['id_token'] = token_stub - session['user'] = 'userinfo stub' - - parsed_redirect_uri = parse.urlparse(cfg.REDIRECT_URIS[0]) - post_logout_uri = '%s://%s' % (parsed_redirect_uri.scheme, parsed_redirect_uri.netloc) - - expected_query = 'post_logout_redirect_uri=%s&token_hint=%s' % (post_logout_uri, token_stub) - response = self.client.get(url_for('logout')) - - parsed_location = parse.urlparse(response.location) - assert parsed_location.query == expected_query - diff --git a/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py b/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py deleted file mode 100644 index 68f7012c88e..00000000000 --- a/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py +++ /dev/null @@ -1,68 +0,0 @@ -from clientapp import create_app, session -from flask import Flask, url_for -from typing import List -from werkzeug import local -from helper import FlaskBaseTestCase - - -def app_endpoint(app: Flask) -> List[str]: - """ Return all enpoints in app """ - - endpoints = [] - for item in app.url_map.iter_rules(): - endpoint = item.endpoint.replace("_", "-") - endpoints.append(endpoint) - return endpoints - - -class TestProtectedContentEndpoint(FlaskBaseTestCase): - def test_app_should_contain_protected_content_route(self): - - endpoints = app_endpoint(create_app()) - self.assertIn('protected-content', endpoints, - 'protected-content route not found in app endpoints') - - def test_app_protected_content_route_should_return_valid_requisition(self): - - self.client.get(url_for('protected_content')) - - self.assertIn( - self.client.get(url_for('protected_content')).status_code, - range(100, 511), - 'protected content route returned invalid requisition') - - def test_should_return_if_session_exists_in_clientapp(self): - import clientapp - self.assertTrue(hasattr(clientapp, 'session'), - "session is not an attribute of clientapp") - del clientapp - - def test_should_check_if_session_is_LocalProxy_instance(self): - self.assertIsInstance(session, local.LocalProxy) - - def test_protected_content_return_status_200_ir_session_profile_exists( - self): - - with self.client.session_transaction() as sess: - sess['user'] = 'foo' - - self.assertEqual( - self.client.get(url_for('protected_content')).status_code, 200) - - def test_should_return_302_if_no_session_profile(self): - self.assertEqual( - self.client.get(url_for('protected_content')).status_code, 302) - - def test_protected_content_should_redirect_to_login_if_session_profile_doesnt_exist( - self): - - response = self.client.get(url_for('protected_content')) - self.assertTrue(response.location.endswith(url_for('login')), - 'Protected page is not redirecting to login page') - - ''' TODO - def test_should_return_if_user_logged_in_exists(self): - self.assertTrue( - hasattr(app,'user_logged_in') - ) - ''' diff --git a/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md b/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md index 57d0a9597f6..df56dce9e18 100644 --- a/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md +++ b/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md @@ -387,7 +387,7 @@ Server deployment ## Test -1. [Setup](https://github.com/JanssenProject/jans/tree/main/demos/jans-tent) Janssen Tent +1. [Setup](https://github.com/JanssenProject/jans/tree/v1.2.0/demos/jans-tent) Janssen Tent 2. Change the configuration as given below in `config.py` ```